根据 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 的。
问题
在框架的分析中,其实遗留了两个问题
- ActivityRecord 和 Task 的 surface,在创建的时候,是隐藏的。那么,它们的 surface 是什么时候显示的呢?
- 窗口层级构建时的 layer 更新,没有为 Task 更新 layer。那么 Task 的 layer 是何时更新的呢?
然而,此时还不能回答这两个问题,因此它们与窗口的刷新,以及 Transition 动画紧密相关。在后续的文章中,我会分析到这两个问题。