乐于分享
好东西不私藏

「安卓framework基础篇11」从WMS到BufferQueue第四篇 - 探索APP的窗口是如何挂载到WMS层级树上(基于AOSP13)

「安卓framework基础篇11」从WMS到BufferQueue第四篇 - 探索APP的窗口是如何挂载到WMS层级树上(基于AOSP13)

「安卓framework基础篇11」从WMS到BufferQueue第四篇 - 探索APP的窗口是如何挂载到WMS层级树上(基于AOSP13)

APP进程的事件循环监听到有onResume状态变更请求消息后,会调用ActivityThread.java的handleResumeActivity方法处理消息:

@Override    public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,            boolean isForward, String reason) {        // 省略        // 这里会调用Activiy的onResume方法        if (!performResumeActivity(r, finalStateRequest, reason)) {            return;        }        // 这里用ActivityClientRecord里的token对象作为key来查询当前Activiy是否在销毁名单里        // 如果在销毁名单里后面的流程就不用继续走了        if (mActivitiesToBeDestroyed.containsKey(r.token)) {          // 省略            return;        }       // 省略        if (r.window == null && !a.mFinished && willBeVisible) {            // 省略            if (a.mVisibleFromClient) {                if (!a.mWindowAdded) {                    a.mWindowAdded = true;                    // 调用WindowManger的addView                    wm.addView(decor, l);                }// 省略            }          // 省略        }         // 省略    }

WindowMangerImpl.java 的 addView 方法:

@Override    public void addView(@NonNullView view, @NonNullViewGroup.LayoutParams params) {        applyTokens(params);        // 调用WindowManagerGlobal的addView方法        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,                mContext.getUserId());    }

WindowManagerGlobal.java 的 addView 方法:

publicvoidaddView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow, int userId) {           // 省略           ViewRootImpl root;           // 省略            if (windowlessSession == null) {                root = new ViewRootImpl(view.getContext(), display);            } else {                root = new ViewRootImpl(view.getContext(), display,                        windowlessSession);            }            // 省略                // 调用 ViewRootImpl 对象的setView方法                root.setView(view, wparams, panelParentView, userId);            } catch (RuntimeException e) {                // BadTokenException or InvalidDisplayException, clean up.                if (index >= 0) {                    removeViewLocked(index, true);                }                throw e;            }        }    }

ViewRootImpl.java的setView方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,                        int userId) {        synchronized (this) {        // 省略                     // 跨进程调用wms服务创建对应的WindowState和                    res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,                            getHostVisibility(), mDisplay.getDisplayId(), userId,                            mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,                            mTempControls);         // 省略        }    }

跨进程会调到 WindowManagerService.java 的 addWindow 方法:

public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,            int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,            InputChannel outInputChannel, InsetsState outInsetsState,            InsetsSourceControl[] outActiveControls) {        // 省略         synchronized (mGlobalLock) {            // 省略             // 使用binder对象作为token查询是否已存在WindowState,如果已存在则直接返回不重复创建了            if (mWindowMap.containsKey(client.asBinder())) {                ProtoLog.w(WM_ERROR, "Window %s is already added", client);                return WindowManagerGlobal.ADD_DUPLICATE_ADD;            }            // 省略             // Use existing parent window token for child windows since they go in the same token            // as there parent window so we can apply the same policy on them.            WindowToken token = displayContent.getWindowToken(                    hasParent ? parentWindow.mAttrs.token : attrs.token);            // 省略             // 如果token为null,说明当前启动的应用不是普通app应用而是系统应用            // 因为只有普通应用有ActivityRecord,而在创建ActivityRecord时会生成一个IBinder类型的唯一token            if (token == null) {                // 省略             } else if (rootType >= FIRST_APPLICATION_WINDOW                    && rootType <= LAST_APPLICATION_WINDOW) {                activity = token.asActivityRecord();                // 省略             }             // 省略             // 创建WindowState对象            final WindowState win = new WindowState(this, session, client, token, parentWindow,                    appOp[0], attrs, viewVisibility, session.mUid, userId,                    session.mCanAddInternalSystemWindow);            // 省略             // 获取触摸事件数据管道            final boolean openInputChannels = (outInputChannel != null                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);            if  (openInputChannels) {                // 将触摸事件数据管道绑定到WindowState对象中                win.openInputChannel(outInputChannel);            }            // 省略            // 将创建的WindowState放入Map中缓存起来,binder作为key            // 省略            // 将WindowState挂载到WindowToken/ActivityRecord节点下            win.mToken.addWindow(win);            // 省略    }

最后以一张时序图总结,时序图最下方红框里的步骤是本篇文章说的内容:

  • 粉色背景的步骤是创建和使用Token的步骤,Token是什么?有什么作用?下篇文章我会跟大家一起探索。
  • 蓝色背景的步骤是尝试调用realStartAcvtivityLocked方法创建Activity的步骤。
  • 绿色背景的步骤是执行Activity生命周期回调方法的地方。