【Android 13源码分析】WindowContainer窗口层级-4-Layer树
在安卓源码的设计中,将将屏幕分为了37层,不同的窗口将在不同的层级中显示。
对这一块的概念以及相关源码做了详细分析,整理出以下几篇。
【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树
【Android 13源码分析】WindowContainer窗口层级-2-构建流程
【Android 13源码分析】WindowContainer窗口层级-3-实例分析
【Android 13源码分析】WindowContainer窗口层级-4-Surface树
本篇为第四篇,前面三篇已经将Android窗口树介绍完了,但是我们知道安卓真正控制显示的是在SurfaceFlinger层,难道说SurfaceFlinger层也有这么一个窗口树吗?如果有,Framework层构建窗口树的代码这么复杂,难道SurfaceFlinger也有这么一段复杂的逻辑吗?
首先回答第一个问题:SurfaceFlinger层也有这么一个窗口树,严格来说是SurfaceFlinger也有一个对应的Layer树。
这是使用Winscope工具看到的当前屏幕信息,可以看到在SurfaceFlinger层也有一个和窗口树应用的层级关系,并且在WindowState层下面还多了一级,多出来的这层右边的属性中有一项“isBuffLayer= true”。
先以黑盒的形式补充几个Surface相关的知识点,对这块有了解的可跳过。
1. Surface知识黑盒概念
-
- 触发创建Surface时就会触发创建出一个Layer,所以Surface和Layer是一一对应的,只不过在framework层侧重Surface,在SurfaceFlinger侧重Layer。
-
- 应用层只要有Surface,就可以将View的数据绘制保存到Surface中,也就可以显示到屏幕上
-
- Layer有多种类型,最常见的是“容器类型”和“buff类型”,只有“buff类型”的Layer才能保存UI数据。
2 容器类型的创建
前面几篇介绍窗口树的时候,知道那些类其实都是“容器”,作为容器他们本身是没有UI数据的,真正有显示数据的就是 “isBuffLayer= true”的这层Layer。
再回答第二个问题:SurfaceFlinger没有这么复杂构建Layer树的逻辑,因为只要Framework创建一个“容器”类的同时也触发创建一个Surface,这样SurfaceFlinger层就也能同步构造出一个Layer(Surface)树。
2.1 DisplayContent的Surface构建
首先看一下 屏幕(DisplayContent)对应的Surface是怎么创建的。
在构建流程开始的时候就为 DisplayContent的创建了Surface,代码如下:
# DisplayContent
private void configureSurfaces(Transaction transaction) {
// 构建一个SurfaceControl
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
.setContainerLayer() // 设置为容器类型的Layer
.setCallsite("DisplayContent");
// 设置名字后构建 (Display 0 name="XXX")
mSurfaceControl = b.setName(getName()).setContainerLayer().build();
// 设置策略并构建显示区域层次结构
if (mDisplayAreaPolicy == null) {
// WMS的getDisplayAreaPolicyProvider方法按返回 DisplayAreaPolicy.Provider
// 然后其 instantiate的实现 目前只有DisplayAreaPolicy的内部类DefaultProvider
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */,
mImeWindowsContainer);
}
......
}
在构建窗口树源码分析的时候知道DisplayContent::configureSurfaces 是在 DisplayContent构造方法里执行的,也就是说在构建窗口树的时候,创建了DisplayContent容器的同时,也创建好了对应的Surface,这是第一个映射关系。
注意build的时候 setContainerLayer这个设置, 将其设置为容器类型的Layer
2.2 其他容器Surface的创建与挂载
层级树其他的各个容器创建的也会触发创建出对应的一个Surface,具体的调用链如下:
WindowContainer::addChild
WindowContainer::setParent
WindowState::onParentChanged
WindowContainer::createSurfaceControl
WindowContainer::setInitialSurfaceControlProperties
SurfaceControl.Builder::build
SurfaceControl::init
调用链从WindowContainer::addChild 开始是因为每个容器类创建的时候,都会挂载到父节点下,挂载的方式也就是执行WindowContainer::addChild方法,添加到父容器的孩子集合下。
以一个应用的Task容器创建挂载为例:
假设其他窗口树已经构建好(也已经有了一个对应的Layer树,暂时不知道是怎么来的没关系,稍后就明白了),这个时候应用启动了,肯定是需要创建一个Task,Task创建好后是还是一个单独的容器,这个时候会执行 WindowContainer::addChild 和 WindowContainer::setParent方法,执行完后Task就挂着到窗口树上了。
先看一下这2个方法的代码:
# WindowContainer
// 当前容器的孩子容器集合
protected final WindowList<E> mChildren = new WindowList<E>();
@CallSuper
protected void addChild(E child, Comparator<E> comparator) {
......
// 1. 添加到集合中,也就是挂载
if (positionToAdd == -1) {
mChildren.add(child);
} else {
mChildren.add(positionToAdd, child);
}
// Set the parent after we've actually added a child in case a subclass depends on this.
// 2. 调用孩子容器设置父节点的方法
child.setParent(this);
}
首先将子容器添加到 mChildren 集合中,然后调用子容器的 setParent 方法。 这么2步执行后, 孩子与父容器就有绑定关系了,也就是成功挂载到了父节点执行。(细品,其实就是java集合操作)
先看一下这2个方法的代码:
# WindowContainer
// 当前容器的孩子容器集合
protected final WindowList<E> mChildren = new WindowList<E>();
@CallSuper
protected void addChild(E child, Comparator<E> comparator) {
......
// 1. 添加到集合中,也就是挂载
if (positionToAdd == -1) {
mChildren.add(child);
} else {
mChildren.add(positionToAdd, child);
}
// Set the parent after we've actually added a child in case a subclass depends on this.
// 2. 调用孩子容器设置父节点的方法
child.setParent(this);
}
首先将子容器添加到 mChildren 集合中,然后调用子容器的 setParent 方法。 这么2步执行后, 孩子与父容器就有绑定关系了,也就是成功挂载到了父节点执行。(细品,其实就是java集合操作)
setParent 方法具体代码如下:
# WindowContainer
// 父节点
private WindowContainer<WindowContainer> mParent = null;
final protected void setParent(WindowContainer<WindowContainer> parent) {
if (parent == null) {
Slog.d(TAG, "setParent old=" + mParent + ",new=" + parent + ",this window=" +
this + ",callers=" + Debug.getCallers(6));
}
final WindowContainer oldParent = mParent;
mParent = parent;
......
onParentChanged(mParent, oldParent);
......
}
现在 Task 就成功找到组织了,挂着到窗口树上了。
但是这个时候,SurfaceFlinger那边还是没变化的,所以继续看后续流程。
# WindowContainer
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent,
PreAssignChildLayersCallback callback) {
super.onParentChanged(newParent, oldParent);
// 正常肯定是有父节点的
if (mParent == null) {
return;
}
if (mSurfaceControl == null) {
// If we don't yet have a surface, but we now have a parent, we should
// build a surface.
// 父亲有了,但是自身还为null,则触发创建自身Surface的逻辑
createSurfaceControl(false /*force*/);
} else {
......// 有则进行 reparent
reparentSurfaceControl(getSyncTransaction(), mParent.mSurfaceControl);
}
......
}
// 重点,触发Surface的创建
void createSurfaceControl(boolean force) {
setInitialSurfaceControlProperties(makeSurface());
}
注释比较详细就不多说了,这里肯定是走createSurfaceControl()逻辑,然后注意makeSurface()方法会创建出Surface,然后再调用setInitialSurfaceControlProperties。先看 makeSurface 方法
# WindowContainer
// 当前容器的Surface
protected SurfaceControl mSurfaceControl;
SurfaceControl.Builder makeSurface() {
// 拿到父节点,调用makeChildSurface
final WindowContainer p = getParent();
// 传递当前,也就是Task
return p.makeChildSurface(this);
}
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
// 拿到父亲
final WindowContainer p = getParent();
// Give the parent a chance to set properties. In hierarchy v1 we rely
// on this to set full-screen dimensions on all our Surface-less Layers.
// 调用父亲的makeChildSurface方法,再调用setParent
return p.makeChildSurface(child)
.setParent(mSurfaceControl);
}
这里方法虽然不多,但是逻辑有点绕,做一下解释:
-
- 是子容器调用的 makeChildSurface 方法,那子容器类就是 Task,父容器就是 DefaultTaskDisplayArea
-
- 执行 父容器 makeChildSurface方法的时候,又调用了getParent 获取父容器,执行 makeChildSurface,(眉头一皱,事情并不简单)这是开始递归了。
-
- 先不管递归,总之肯定的是 makeChildSurface方法不管怎么递归返回的还是一个SurfaceControl.Builder,然后调用setParent将DefaultTaskDisplayArea的Surface设置为其父节点。
这样一来,结果就是 :Task调用 父容器的makeChildSurface后,创建出了一个Surface,并且挂载到了父容器(DefaultTaskDisplayArea)的下面。
知道结果后,还是要弄清楚 递归方法是怎么创建 Task对应的Surface的
-
- 对于递归调用,最终要的就是找到递归结束的条件,当前这个递归结束的条件就是 DisplayContent 类重写了makeChildSurface方法,也就是说调到 DisplayContent::makeChildSurface 就意味着递归的结束。
DisplayContent作为一个屏幕的最底层的容器,肯定是会调用到的,毕竟 DefaultTaskDisplayArea也是挂载在这个树上的。
所以现在来看看 DisplayContent::makeChildSurface方法
# DisplayContent
@Override
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
SurfaceSession s = child != null ? child.getSession() : getSession();
// 创建一个容器类型 Surface的Builder
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
if (child == null) {
return b;
}
// 设置容器名
return b.setName(child.getName())
.setParent(mSurfaceControl);
}
这里的参数 child 就是Task,
-
- 首先创建一个容器类型 SurfaceControl.Builder
-
- 设置name,当前场景是把Task的名字设置过去
-
- 然后设置一下父亲为DisplayContent的Surface
这里要注意,这里设置父节点最终是无效的,会被覆盖掉,因为上面分析看到了把 DefaultTaskDisplayArea设置为Task父容器。从代码的执行顺序上来说,DisplayContent的这次setParent先执行,会被后面的覆盖掉。 从结果来看,Task也确实是挂在DefaultTaskDisplayArea下的。 (不可能每个容器都直接作为DisplayContent的子节点)
调用链执行完了,SurfaceFlinger层也创建并且挂载好了Task的Surface。
到这里,Framework层的窗口树, SurfaceFlinger的Surface树构建的差不多了,但是手机上还是不会有内容的,为什么呢? 因为这些都是 “容器”,真正的显示需要有Buff类型的Surface。
再看一次这个图, 对应的窗口树到了WindowState就结束了, SurfaceFliner 这边可以看到WindowState下还有一个节点,这个节点才是真正有UI数据的 Layer。
需要体会一下区别
在Activity启动流程中时执行到目标应用进程创建时会触发Task和ActivityRecord创建和挂载。 这个时候WindowState还没出现,另外到这一步Activity的onCreate也没执行到,所以界面上肯定是没有UI显示的。
Activity进程创建后,会先执行[addWindow流程]触发 WindowState的创建和挂载,但是这步执行完也还是没有画面的, 因为WindowState也是一个“容器”。
真正触发显示图层创建的是在【relayoutWindow】流程,具体的流程不是当前的主题,目前只关注【relayoutWindow】流程中“Buff”类型图层的创建。
3.1 流程概览
这里才是第一次执行relayoutWindow 创建真正显示的surface的地方
relayoutWindow的调用链如下:
WindowManagerService::relayoutWindow
WindowManagerService::createSurfaceControl
WindowStateAnimator::createSurfaceLocked -- 创建“Buff” 类型Surface
WindowStateAnimator::resetDrawState -- 设置窗口状态为DRAW_PENDING
WindowSurfaceController::init
SurfaceControl.Builder::build
SurfaceControl::init
WindowSurfaceController::getSurfaceControl -- 给应用端Surface赋值
开始撸代码
# WindowManagerService
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) {
......
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
......
}
createSurfaceControl 方法有4个参数:
- outSurfaceControl: WMS创建好一个Surface后,还需要返回给应用端用于View的绘制,就是通过这个参数,由参数命名也可以知道这是一个“出参”。
- result: 方法执行结果
- win: 当前窗口对应的WindowState,稍后创建Surface会挂载到这个WindowState节点之下
- winAnimator:WindowStateAnimator对象,管理窗口状态和动画,稍后通过其内部方法创建Surface
# WindowManagerService
private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
WindowState win, WindowStateAnimator winAnimator) {
// 1. 创建WindowSurfaceController对象
WindowSurfaceController surfaceController;
try {
// 2. 创建“Buff”类型Surface
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
surfaceController = winAnimator.createSurfaceLocked();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (surfaceController != null) {
// 3. 出参给应用端
surfaceController.getSurfaceControl(outSurfaceControl);
// 打印日志,outSurfaceControl复制到了framework的值
ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
}......
return result;
}
这个方法主要有三步,都是围绕着 WindowSurfaceController 来的:
-
- 先创建出一个WindowSurfaceController 对象 surfaceController
-
- 通过WindowStateAnimator::createSurfaceLocked 对 surfaceController 赋值,根据方法名猜测是创建了一个Surface
-
- 通过 WindowSurfaceController::getSurfaceControl,给应用端 Surface 赋值
这么看来重点是在第二步 WindowStateAnimator::createSurfaceLocked 是如何创建Surface的。
# WindowStateAnimator
WindowSurfaceController mSurfaceController;
// WindowState的状态
int mDrawState;
WindowSurfaceController createSurfaceLocked() {
final WindowState w = mWin;
if (mSurfaceController != null) {
return mSurfaceController;
}
w.setHasSurface(false);
// 打印窗口状态
ProtoLog.i(WM_DEBUG_ANIM, "createSurface %s: mDrawState=DRAW_PENDING", this);
// 重点* 1. 重置窗口状态
resetDrawState();
......
// 重点* 2. 创建WindowSurfaceController
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
flags, this, attrs.type);
......
return mSurfaceController;
}
这里有2个重点:
-
- 设置窗口状态为 DRAW_PENDING
-
- 创建Surface
3.2 设置窗口状态–DRAW_PENDING
# WindowStateAnimator
void resetDrawState() {
// 设置windowState状态为DRAW_PENDING
mDrawState = DRAW_PENDING;
if (mWin.mActivityRecord == null) {
return;
}
if (!mWin.mActivityRecord.isAnimating(TRANSITION)) {
mWin.mActivityRecord.clearAllDrawn();
}
}
WindowState有很多状态,以后会单独说,这里需要注意
- WindowState状态是保存在WindowStateAnimator中
- WindowStateAnimator::createSurfaceLocked方法会将WindowState状态设置为DRAW_PENDING,表示等待绘制。
3.3 创建“Buff”类型Surface
继续回到主流程,看看 WindowSurfaceController 的构造方法
# WindowSurfaceController
SurfaceControl mSurfaceControl;
WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,
int windowType) {
mAnimator = animator;
// 1. 也会作为Surface的name
title = name;
mService = animator.mService;
// 2. 拿到WindowState
final WindowState win = animator.mWin;
mWindowType = windowType;
mWindowSession = win.mSession;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
// 3. 重点* 构建Surface(也是通过makeSurface 方法)
final SurfaceControl.Builder b = win.makeSurface()
.setParent(win.getSurfaceControl()) // 设置为父节点
.setName(name)
.setFormat(format)
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
.setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
.setCallsite("WindowSurfaceController");
final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
// 高版本都为BLAST
if (useBLAST) {
// 4. 重点* 设置为“Buff”图层
b.setBLASTLayer();
}
// 触发build
mSurfaceControl = b.build();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
这个方法有4个点
- 第一个参数传递的字符串最终也会作为Surface的name
- 获取到WindowState对象,后面会设置为创建Surface的父节点
- 构建出一个Surface对象, 注意name和 父节点的设置。 另外可以知道也是通过makeSurface()方法构建的, 这个方法在 2.1小结看到是构建出一个“容器”类型的Surface。
- 将Surface设置为“Buff”类型,这个非常重要,因为上一步默认还是“容器”类型,所以需要设置成“Buff”类型,再后面就是build出一个Surface了
那么到这里Surface的创建就完成了,这里可能有的人如果对Surface知识不太清楚的话会比较迷糊,WindowSurfaceController,SurfaceController,Surface到底是什么关系,这个不在当前流程的重点,暂且理解为同级吧,有WindowSurfaceController就可以拿到内部的SurfaceController,而SurfaceController又可以获取到Surface。
3.4 返回Surface到应用端
最后再来看一下 WMS这边创建好后的Surface是如何设置给应用端的。
应用端View的绘制信息都是保存到Surface上的,因为必定要有一个"Buff"类型的Surface,也就是上面流程中创建的这个Surface。
应用端的ViewRootImpl触发WMS的relayoutWindow会传递一个出参 :outSurfaceControl过来, 现在WMS会通过以下方法将刚刚创建好是Surface传递到应用端。
这样一来应用端就有了可以保持绘制数据的Surface,然后就可以执行 View::draw。
# WindowSurfaceController
void getSurfaceControl(SurfaceControl outSurfaceControl) {
// 将framework层的SurfaceControl copy给应用层传递过来的outSurfaceControl
outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
}