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 动画紧密相关。在后续的文章中,我会分析到这两个问题。

相关推荐
_平凡之路_18 分钟前
实现限制同一个账号最多只能在3个客户端(有电脑、手机等)登录(附关键源码)
android·java·vue.js·spring·servlet
guodashen0072 小时前
安卓使用JExcelApi读取Excel文件
android·excel·jexcelapi
if就3 小时前
Java_多线程
android·java·开发语言
键盘被我撸冒烟了4 小时前
Android Knowledge
android
0wioiw04 小时前
Android逆向(Python和JS通信)
android·javascript·python
双鱼大猫6 小时前
一句话说透kotlin里面的内联和高阶函数
android·kotlin
北执南念6 小时前
Synchronized使用
android·java·jvm
新玉54016 小时前
DVWA靶场
android·java·数据库·php
0wioiw09 小时前
安卓基础(Okhttp3)
android