Android V app 冷启动(2) 窗口层级的构建

根据 Android V app 冷启动 (1) Activity生命周期 分析,在第一阶段启动中,会构建窗口层级。但是,我打算以授人以渔的方式来讲解,即分析框架,而不是分析具体的流程。

框架

在窗口层级构建时,parent 都是通过基类的 WindowContainer#addChild() 来保存 child

java 复制代码
// WindowContainer.java

// parent 保存 child
void addChild(E child, int index) {
    // ...

    // parent 的数据结构 mChildren 保存 child
    mChildren.add(index, child);
    
    // child 关联 parent
    child.setParent(this);
}
java 复制代码
// WindowContainer.java

// child 关联 parent
final protected void setParent(WindowContainer<WindowContainer> parent) {
    final WindowContainer oldParent = mParent;

    // 1. child 保存 parent
    mParent = parent;

    // ...

    if (!mReparenting) {

        // 处理 sync state
        onSyncReparent(oldParent, mParent);

        if (mParent != null && mParent.mDisplayContent != null
                && mDisplayContent != mParent.mDisplayContent) {
            // 保存 DisplayContent
            onDisplayChanged(mParent.mDisplayContent);
        }

        // 2. child 处理 parent 改变
        onParentChanged(mParent, oldParent);
    }
}    


void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
    // 利用新 parent 的 full config,更新 child 的 config
    super.onParentChanged(newParent, oldParent);

    if (mParent == null) {
        return;
    }

    if (mSurfaceControl == null) {
        // 3. 既然有了 parent,那么就创建 child 的 surface,并挂在 parent surface 之下
        createSurfaceControl(false /*force*/);
    } else {
        // ...
    }

    // 4. 通过 parent 重新调整 children layer
    mParent.assignChildLayers();
}

从这里可以看出,窗口层级构建,包括 WMS 层和 surface 层。在 WMS 层,是通过数据结构相互关联,而在 surface 层,需要 child 窗口 surface,并挂载到 parent 之下。最后,还通过 parent 重新调整 children 窗口的 layer。

创建并挂载 child surface

java 复制代码
// WindowContainer.java

// child 创建 surface

void createSurfaceControl(boolean force) {
    // makeSurface() 是创建一个 SurfaceControl.Builder
    setInitialSurfaceControlProperties(makeSurface());
}

void setInitialSurfaceControlProperties(Builder b) {
    // 用 mSurfaceControl 保存创建的 surface
    setSurfaceControl(b.setCallsite("WindowContainer.setInitialSurfaceControlProperties").build());
    
    // 只有 ActivityRecord 和 Task 的 surface,不需要在创建时立即 show 出来
    if (showSurfaceOnCreation()) { 
        getSyncTransaction().show(mSurfaceControl);
    }
    
    // 更新 surface position
    updateSurfacePositionNonOrganized();
    
    // ...
}

child surface 在创建完成后,除了 ActivityRecord 和 Task 以外,都是立即通过 sync transaction/pending transaction show 出来。

也就是说,ActivityRecord 和 Task 的 surface 在刚创建时,是不可见的。这与 Transition 动画有关,这里暂时不表。

makeSurface() 在创建 SurfaceControl.Builder 的同时,还会为 child surface 设置 parent surface,如下

java 复制代码
// WindowContainer.java

Builder makeSurface() {
    final WindowContainer p = getParent();
    // 通过 parent 来构建 SurfaceControl.Builder
    return p.makeChildSurface(this);
}    

Builder makeChildSurface(WindowContainer child) {

    // parent 也递归调用自己 parent 来构建 Builder
    final WindowContainer p = getParent();
    return p.makeChildSurface(child)
            // 注意,这里指定了 child surface 的 parent surface
            // 其实就是 child 的 parent 的 surface
            .setParent(mSurfaceControl);
}    

child 的 SurfaceControl.Builder 是通过 parent 进行构建,并且还把 parent surface 指定为 child surface 的 parent。

经过层层递归,最终由 DisplayContent 来创建 child 的 SurfaceControl.Builder

java 复制代码
// DisplayContent.java

SurfaceControl.Builder makeChildSurface(WindowContainer child) {
    SurfaceSession s = child != null ? child.getSession() : getSession();

    final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s)
                                                .setContainerLayer();
    if (child == null) {
        return b;
    }
    return b.setName(child.getName())
            .setParent(mSurfaceControl);
}

parent 调整 children layer

当窗口层级构建完成后,parent 会调整所有 children layer,包括 WMS layer 以及 surface layer

java 复制代码
// WindowContainer.java

void assignChildLayers() {
    // 注意,layer 的调整,是通过 sync transaction 或 pending transaction 执行的
    assignChildLayers(getSyncTransaction());
    scheduleAnimation();
}

void assignChildLayers(Transaction t) {
    int layer = 0;

    
    for (int j = 0; j < mChildren.size(); ++j) {
        final WindowContainer wc = mChildren.get(j);
        // 递归调用,让 child 更新自己 children layer
        wc.assignChildLayers(t);
        
        // 1.优先给不需要提升z-order的 child 更新 layer
        if (!wc.needsZBoost()) {
            wc.assignLayer(t, layer++);
        }
    }
    
    
    for (int j = 0; j < mChildren.size(); ++j) {
        final WindowContainer wc = mChildren.get(j);
        // 2.然后在给需要提升z-order的 child 更新 layer
        if (wc.needsZBoost()) {
            wc.assignLayer(t, layer++);
        }
    }
    if (mOverlayHost != null) {
        mOverlayHost.setLayer(t, layer++);
    }
}

parent 调整 children layer 的原理是,遍历 mChildren,依次给 child 更新 layer(从 0 开始)。

来看下具体更新 layer 是如何实现的

java 复制代码
// WindowContainer.java

void assignLayer(Transaction t, int layer) {

    // 排除不能更新 layer 的情况
    // 例如,动画在执行的时候,不能更新 Task / ActivityRecord 的 layer,
    // 因为它们的 surface 可能正在被动画操纵
    if (!mTransitionController.canAssignLayers(this)) return;
    
    // layer 有改变
    final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
    
    if (mSurfaceControl != null && changed) {
        // 设置 layer
        setLayer(t, layer);
        
        // 保存最新 layer
        mLastLayer = layer;
        mLastRelativeToLayer = null;
    }
}

这里先不谈不能更新 layer 的情况,只要 layer 改变,就需要重新设置 layer

java 复制代码
// WindowContainer.java

protected void setLayer(Transaction t, int layer) {
    if (mSurfaceFreezer.hasLeash()) {
        // When the freezer has created animation leash parent for the window, set the layer
        // there instead.
        mSurfaceFreezer.setLayer(t, layer);
    } else {
        // Route through surface animator to accommodate that our surface control might be
        // attached to the leash, and leash is attached to parent container.
        mSurfaceAnimator.setLayer(t, layer);
    }
}
java 复制代码
// SurfaceFreezer.java

void setLayer(SurfaceControl.Transaction t, int layer) {
    if (mLeash != null) {
        // 给 leash 设置 layer
        t.setLayer(mLeash, layer);
    }
}
java 复制代码
// SurfaceAnimator.java

void setLayer(Transaction t, int layer) {
    // 有 leash,就给 leash 设置 layer,否则就给自己 surface 设置 layer
    t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
}

这里的代码,我个人认为比较晦涩,它的意思其实很简单,如果 WindowContainer 有动画在执行,就给动画的 leash 设置 layer,否则就给 WindowContainer 自己的 surface 设置 layer。

更新 layer 的条件

这里,针对 app 冷启动的情况,来看下窗口层级构建时,检测是否能设置 layer 的方法

java 复制代码
// TransitionController.java

boolean canAssignLayers(@NonNull WindowContainer wc) {
    // Don't build window state into finish transaction in case another window is added or
    // removed during transition playing.
    if (mBuildingFinishLayers) {
        return wc.asWindowState() == null;
    }
    
    // Always allow WindowState to assign layers since it won't affect transition.
    return wc.asWindowState() != null || (!isPlaying()
            // Don't assign task while collecting.
            && !(wc.asTask() != null && isCollecting()));
}

根据 Android V app 冷启动 (1) Activity生命周期 分析,在第一阶段启动中,首先创建不 Transition 并进入收集状态,然后收集了要启动的 ActivityRecord。

那么,系统当前有一个处于收集状态的 Transition。因此,窗口层级构建中,把 Task 保存到 TaskDisplayArea 时,是不能为 Task 更新 layer 的。而把 ActivityRecord 保存到 Task 时,是可以为 ActivityRecord 更新 layer 的。

问题

在框架的分析中,其实遗留了两个问题

  1. ActivityRecord 和 Task 的 surface,在创建的时候,是隐藏的。那么,它们的 surface 是什么时候显示的呢?
  2. 窗口层级构建时的 layer 更新,没有为 Task 更新 layer。那么 Task 的 layer 是何时更新的呢?

然而,此时还不能回答这两个问题,因此它们与窗口的刷新,以及 Transition 动画紧密相关。在后续的文章中,我会分析到这两个问题。

相关推荐
QING61819 分钟前
Kotlin containsValue用法及代码示例
android·kotlin·源码阅读
QING61823 分钟前
Kotlin coerceAtMost用法及代码示例
android·kotlin·源码阅读
QING61826 分钟前
Kotlin commonSuffixWith用法及代码示例
android·kotlin·源码阅读
QING61828 分钟前
Kotlin coerceAtLeast用法及代码示例
android·kotlin·源码阅读
光军oi1 小时前
Mysql从入门到精通day5————子查询精讲
android·数据库·mysql
鸿蒙布道师10 小时前
鸿蒙NEXT开发Base64工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
jiet_h11 小时前
Android adb 的功能和用法
android·adb
美狐美颜sdk12 小时前
美颜SDK兼容性挑战:如何让美颜滤镜API适配iOS与安卓?
android·深度学习·ios·美颜sdk·第三方美颜sdk·视频美颜sdk
居然是阿宋12 小时前
深入理解 YUV 颜色空间:从原理到 Android 视频渲染
android·音视频
KevinWang_13 小时前
DialogFragment 不适合复用
android