WindowContainer窗口层级-3-实例分析

以应用窗口和系统窗口2大类型窗口的挂载为例介绍窗口是如何挂载到层级树中的。

1. 应用窗口挂载

应用启动流程中会触发ActivityRecord,Task,WindowState的创建与挂载,其中WindowState的处理上在addWindow流程。

ActivityTaskManagerService::startActivity ActivityTaskManagerService::startActivityAsUser ActivityTaskManagerService::startActivityAsUser ActivityStartController::obtainStarter ActivityStarter::execute ActivityStarter::executeRequest -- 构建 ActivityRecord --2.2.1 创建ActivityRecord ActivityStarter::startActivityUnchecked ActivityStarter::startActivityInner -- 2.2.2 关键函数startActivityInner ActivityStarter::getOrCreateRootTask -- 2.2.2.1 创建或者拿到Task ActivityStarter::setNewTask -- 2.2.2.2 将task与activityRecord 绑定 RootWindowContainer::resumeFocusedTasksTopActivities --2.2.2.3 显示Activity

addWindow流程调用链: WindowManagerImpl::addView --- 创建ViewRootImpl WindowManagerGlobal::addView

ViewRootImpl::setView --- 与WMS通信 addView Session.addToDisplayAsUser WindowManagerService::addWindow WindowState::init -- WindowState的创建 WindowToken::addWindow -- WindowState的挂载

1.1 ActivityRecord的创建

启动流程开始的时候会执行到ActivityStarter::executeRequest,在这个方法里会创建一个ActivityRecord

scss 复制代码
# ActivityStarter
  private int executeRequest(Request request) {
        ......
        final ActivityRecord r = new ActivityRecord.Builder(mService)
                .setCaller(callerApp)
                .setLaunchedFromPid(callingPid)
                .setLaunchedFromUid(callingUid)
                .setLaunchedFromPackage(callingPackage)
                .setLaunchedFromFeature(callingFeatureId)
                .setIntent(intent)
                .setResolvedType(resolvedType)
                .setActivityInfo(aInfo)
                .setConfiguration(mService.getGlobalConfiguration())
                .setResultTo(resultRecord)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setComponentSpecified(request.componentSpecified)
                .setRootVoiceInteraction(voiceSession != null)
                .setActivityOptions(checkedOptions)
                .setSourceRecord(sourceRecord)
                .build();
        ......
        // 继续执行startActivityUnchecked
        mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
                request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,
                inTask, inTaskFragment, restrictedBgActivity, intentGrants);
        ......
  }

tips: ActivityRecord的构造方法会创建一个Token,这个token就是阅读源码经常看到看到代表activity的那个token。

1.2 Task的创建与挂载

流程开始会执行到ActivityStarter::startActivityInner方法,在这里会执行ActivityStarter::getOrCreateRootTask方法来创建(获取)一个Task 调用链如下: ActivityStarter::getOrCreateRootTask RootWindowContainer::getOrCreateRootTask RootWindowContainer::getOrCreateRootTask TaskDisplayArea::getOrCreateRootTask TaskDisplayArea::getOrCreateRootTask Task::Build ---创建Task 主流程代码

less 复制代码
# ActivityStarter
    private Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task,
            ActivityOptions aOptions) {
        final boolean onTop =
                (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
        final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null;
        return mRootWindowContainer.getOrCreateRootTask(r, aOptions, task, sourceTask, onTop,
                mLaunchParams, launchFlags);
    }
// onTop 表示是否要移到到当前栈顶,那肯定是要的,新启动的Activity当前要再最上面,这里 aOptions 为null,所以为true
// sourceTask 表示从哪里启动的,当前launch所在的Task 就是sourceTask

# RootWindowContainer
    Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
            @Nullable Task candidateTask, boolean onTop) {
        return getOrCreateRootTask(r, options, candidateTask, null /* sourceTask */, onTop,
                null /* launchParams */, 0 /* launchFlags */);
    }
    Task getOrCreateRootTask(@Nullable ActivityRecord r,
            @Nullable ActivityOptions options, @Nullable Task candidateTask,
            @Nullable Task sourceTask, boolean onTop,
            @Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {
            ......
            final int activityType = resolveActivityType(r, options, candidateTask);
            if (taskDisplayArea != null) {
                if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) {
                    // 重点*1. 传递到TaskDisplayArea
                    return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
                            sourceTask, launchParams, launchFlags, activityType, onTop);
                } else {
                    taskDisplayArea = null;
                }
            }
            ......
    }
// 经过同名调用后,逻辑进入到  TaskDisplayArea

# TaskDisplayArea
    Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
            @Nullable Task candidateTask, @Nullable Task sourceTask,
            @Nullable ActivityOptions options, int launchFlags) {
            if(....) {
                // 拿到之前创建的Task
                return candidateTask.getRootTask();
            }
            ......// 第一次显示所以是新建Task
            return new Task.Builder(mAtmService)
                .setWindowingMode(windowingMode)
                .setActivityType(activityType)
                .setOnTop(onTop)
                .setParent(this)  // 主要这个this被设置为Parent。所以直接挂载到了DefaultTaskDisplayArea下
                .setSourceTask(sourceTask)
                .setActivityOptions(options)
                .setLaunchFlags(launchFlags)
                .build();
    }
// 看方法名是获取或创建Task, 这边是新启动的Activity所以需要创建Task。如果是以默认启动方式打开应用内的另一个Activity,就走的是上面的 return candidateTask.getRootTask();
接下来就是真正触发Task的创建。
// 另外设置的parent就是层级结构树应用所在的名为"DefaultTaskDisplayArea"的TaskDisplayArea
# Task
    # Task.Builder
        Task build() {
            if (mParent != null && mParent instanceof TaskDisplayArea) {
                validateRootTask((TaskDisplayArea) mParent);
            }

            if (mActivityInfo == null) {
                mActivityInfo = new ActivityInfo();
                mActivityInfo.applicationInfo = new ApplicationInfo();
            }

            mUserId = UserHandle.getUserId(mActivityInfo.applicationInfo.uid);
            mTaskAffiliation = mTaskId;
            mLastTimeMoved = System.currentTimeMillis();
            mNeverRelinquishIdentity = true;
            mCallingUid = mActivityInfo.applicationInfo.uid;
            mCallingPackage = mActivityInfo.packageName;
            mResizeMode = mActivityInfo.resizeMode;
            mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();
            if (mActivityOptions != null) {
                mRemoveWithTaskOrganizer = mActivityOptions.getRemoveWithTaskOranizer();
            }
            // 重点* 1. 创建task
            final Task task = buildInner();
            task.mHasBeenVisible = mHasBeenVisible;

            // Set activity type before adding the root task to TaskDisplayArea, so home task can
            // be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().
            if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
                task.setActivityType(mActivityType);
            }
            // 重点* 2. 入栈 这里的 mOnTop为true
            if (mParent != null) {
                if (mParent instanceof Task) {
                    final Task parentTask = (Task) mParent;
                    parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,
                            (mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
                } else {
                    mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);
                }
            }

            // Set windowing mode after attached to display area or it abort silently.
            if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
                task.setWindowingMode(mWindowingMode, true /* creating */);
            }
            // 返回
            return task;
        }

        // 创建
        Task buildInner() {
            return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,
                    mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,
                    mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,
                    mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData,
                    mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid,
                    mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
                    mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,
                    mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer,
                    mLaunchCookie, mDeferTaskAppear, mRemoveWithTaskOrganizer);
        }

小结: 最后描述一下最后创建的2个重点部分:

  1. 看到通过buildInner 创建了一个task,而buildInner 也很简单粗暴,通过各个变量直接new Task 对象。
  2. mParent 不为null, 是 因为在创建的时候 setParent(this),当前的这个this,就是 getDefaultTaskDisplayArea返回的也就是应用Activity存在的"DefaultTaskDisplayArea"。

在 RootWindowContainer::getOrCreateRootTask 体现。

注意log里的 #17 的这个Task,与前面的层级结构树新增的Task,是对应的上的。而且this= DefaultTaskDisplayArea 说明也确实是往DefaultTaskDisplayArea里添加了。

1.3 ActivityRecord的挂载

调用链 ActivityStarer::setNewTask ActivityStarer::addOrReparentStartingActivity 主流程代码

java 复制代码
# ActivityStarer
    private void setNewTask(Task taskToAffiliate) {
        // 为true
        final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
        // 就是mTargetRootTask,也就是刚刚创建的Task
        final Task task = mTargetRootTask.reuseOrCreateTask(
                mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
        task.mTransitionController.collectExistenceChange(task);
        // ActivityRecord的挂载
        addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");
        // 需要注意这里的日志打印
        ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",
                mStartActivity, mStartActivity.getTask());

        // mLaunchTaskBehind 为false,所以taskToAffiliate 为null 
        if (taskToAffiliate != null) {
            mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
        }
    }

这里的task 和mTargetRootTask是同一个对象, 进源码跟到流程也是一样。 然后进入 addOrReparentStartingActivity

less 复制代码
# ActivityStarer
    private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
        //  newParent = task 都是刚刚创建的Task
        TaskFragment newParent = task;
        ......
        if (mStartActivity.getTaskFragment() == null
                || mStartActivity.getTaskFragment() == newParent) {
            // 重点, 将 ActivityRecord挂在到新创建的Task中,并且是顶部
            newParent.addChild(mStartActivity, POSITION_TOP);
        } else {
            mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
        }
    }

这里的逻辑设计到的Task就是上一步创建的Task,mStartActivity则是"电话"在之前逻辑创建的ActivityRecord. setNewTask的堆栈信息如下

另外这段逻辑里有个ProtoLog打印,日志如下:

1.4 WindowState的创建与挂载

WindowManagerService::addWindow WindowState::init -- WindowState的创建 WindowToken::addWindow -- WindowState的挂载

java 复制代码
#  WindowManagerService

    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
                ......// token处理
                // 创建WindowState
                final WindowState win = new WindowState(this, session, client, token, parentWindow,
                            appOp[0], attrs, viewVisibility, session.mUid, userId,
                            session.mCanAddInternalSystemWindow);
                ......
                // 7. 窗口添加进容器
                win.attach();
                ......
                win.mToken.addWindow(win);
                ......
            }

"win.mToken"窗口的token是ActyivityRecord

typescript 复制代码
# ActivityRecord
    @Override
    void addWindow(WindowState w) {
        super.addWindow(w);
        ......
    }

直接调用其父类方法,ActivityRecord是WindowToken

scss 复制代码
# WindowToken
    void addWindow(final WindowState win) {
        ProtoLog.d(WM_DEBUG_FOCUS,
                "addWindow: win=%s Callers=%s", win, Debug.getCallers(5));

        if (win.isChildWindow()) {
            // Child windows are added to their parent windows.
            return;
        }
        // This token is created from WindowContext and the client requests to addView now, create a
        // surface for this token.
        // 真正添加进子容器,调用的是WindowContainer的方法
        if (!mChildren.contains(win)) {
            ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
            // 定义在WindowContainer中,其实就是挂载到父容器下了
            addChild(win, mWindowComparator);
            mWmService.mWindowsChanged = true;
            // TODO: Should we also be setting layout needed here and other places?
        }
    }

2. 系统窗口挂载

2.1 WindowToken,WindowState的创建与挂载

java 复制代码
    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
            ......
            // 系统应用获取不到token
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            ......
            if (token == null) {
                ......
                } else {
                    final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                    token = new WindowToken.Builder(this, binder, type)
                            .setDisplayContent(displayContent)
                            .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                            .setRoundedCornerOverlay(isRoundedCornerOverlay)
                            .build();
                }
            }
            // 创建WindowState
                final WindowState win = new WindowState(this, session, client, token, parentWindow,
                            appOp[0], attrs, viewVisibility, session.mUid, userId,
                            session.mCanAddInternalSystemWindow);
            ......
            // 窗口添加进容器
            win.attach();
            ......
            win.mToken.addWindow(win);
            ......

WindowState的创建和应用窗口一样,区别在与WindowToken,系统窗口执行addWindow方法是没有token的,所以会执行创建逻辑。 在创建的时候会根据窗口类型选择挂载的层级。

2.2 WindowToken的挂载

WMS::addWindow WindowToken::init DisplayContent::addWindowToken mTokenMap::put -- 存入mTokenMap DisplayContent::findAreaForToken -- 找到对应的层级 DisplayContent::findAreaForWindowType DisplayAreaPolicyBuilder.Result::findAreaForWindowType RootDisplayArea::findAreaForWindowTypeInLayer -- 在 mAreaForLayer中根据type查找对应的位置

arduino 复制代码
        DisplayArea.Tokens::addChild  -- 挂载

WindowToken的构造方法如下:

java 复制代码
# WindowToken
    protected WindowToken(WindowManagerService service, IBinder _token, int type,
            boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
            boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
        super(service);
        token = _token;
        windowType = type;
        ......
        if (dc != null) {
            // 添加token
            dc.addWindowToken(token, this);
        }
    }

创建WindowToken的时候会由DisplayContent执行挂载逻辑,

scss 复制代码
# DisplayContent
    DisplayAreaPolicy mDisplayAreaPolicy;

    void addWindowToken(IBinder binder, WindowToken token) {
        ......
        // 放入集合
        mTokenMap.put(binder, token);

        if (token.asActivityRecord() == null) {
            ......
            // 找到对应的位置挂载
            final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
            da.addChild(token);
        }
    }

    DisplayArea findAreaForToken(WindowToken windowToken) {
        // 根据type查找
        return findAreaForWindowType(windowToken.getWindowType(), windowToken.mOptions,
                windowToken.mOwnerCanManageAppTokens, windowToken.mRoundedCornerOverlay);
    }

    DisplayArea findAreaForWindowType(int windowType, Bundle options,
            boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
        // 应用类型
        if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
            return mDisplayAreaPolicy.getTaskDisplayArea(options);
        }
        // 输入法窗口
        if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
            return getImeContainer();
        }
        // 其他类型
        return mDisplayAreaPolicy.findAreaForWindowType(windowType, options,
                ownerCanManageAppToken, roundedCornerOverlay);
    }

状态栏不属于应用窗口,走后面的逻辑,DisplayAreaPolicy是个接口,真正的实现是DisplayAreaPolicyBuilder的内部类Result

scala 复制代码
# DisplayAreaPolicyBuilder

   static class Result extends DisplayAreaPolicy {
        final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
        ......
        @Override
        public DisplayArea.Tokens findAreaForWindowType(int type, Bundle options,
                boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
            return mSelectRootForWindowFunc.apply(type, options).findAreaForWindowTypeInLayer(type,
                    ownerCanManageAppTokens, roundedCornerOverlay);
        }
        ......
   }

mSelectRootForWindowFunc是一个存放RootDisplayArea的map,所以后续逻辑在RootDisplayArea中

java 复制代码
// 根据type 找到在容器树的位置, 如果是应用或者输入法都走不到这
# RootDisplayArea
    // 这个就是层级树的
    private DisplayArea.Tokens[] mAreaForLayer;
    @Nullable
    DisplayArea.Tokens findAreaForWindowTypeInLayer(int windowType, boolean ownerCanManageAppTokens,
            boolean roundedCornerOverlay) {
        // 获取到type
        int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
                ownerCanManageAppTokens, roundedCornerOverlay);
        if (windowLayerFromType == APPLICATION_LAYER) {
            throw new IllegalArgumentException(
                    "There shouldn't be WindowToken on APPLICATION_LAYER");
        }
        // 根据type查找对应的位置
        return mAreaForLayer[windowLayerFromType];
    }

getWindowLayerFromTypeLw会根据type找到对应的层级,返回一个int。 然后根据这个值去mAreaForLayer拿到对应的DisplayArea.Tokens,将系统窗口的WindowToken挂载进去 mAreaForLayer其实就是开始构建层级树的那个集合。

2.2.1 mAreaForLayer的赋值

在开机构建窗口层级树的逻辑,最后会执行到RootDisplayArea::onHierarchyBuilt将层级树的集合传递出去。

less 复制代码
# DisplayAreaPolicyBuilder.HierarchyBuilder

        private final RootDisplayArea mRoot;

        private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
            ......// 层级树的构建
            // 通知根节点已经完成了所有DisplayArea的添加 (将displayAreaForLayer保存在RootDisplayArea成员变量roomAreaForLayer中,供后面逻辑使用)
            mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
        }

RootDisplayArea下的mAreaForLayer变量赋值

ini 复制代码
# RootDisplayArea
    private DisplayArea.Tokens[] mAreaForLayer;

    void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer,
            Map<Feature, List<DisplayArea<WindowContainer>>> featureToDisplayAreas) {
        if (mHasBuiltHierarchy) {
            throw new IllegalStateException("Root should only build the hierarchy once");
        }
        mHasBuiltHierarchy = true;
        mFeatures = Collections.unmodifiableList(features);
        // 赋值
        mAreaForLayer = areaForLayer;
        mFeatureToDisplayAreas = featureToDisplayAreas;
    }

所以mAreaForLayer保存了层级树各个层级的对象因此根据index可以获取到对应的DisplayArea.Tokens,并执行系统窗口WindowToken的挂载。

相关推荐
栈老师不回家1 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
帅比九日3 小时前
【HarmonyOS Next】封装一个网络请求模块
前端·harmonyos
bysking3 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
58沈剑3 小时前
80后聊架构:架构设计中两个重要指标,延时与吞吐量(Latency vs Throughput) | 架构师之路...
架构