【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知识黑盒概念

    1. 触发创建Surface时就会触发创建出一个Layer,所以Surface和Layer是一一对应的,只不过在framework层侧重Surface,在SurfaceFlinger侧重Layer。
    1. 应用层只要有Surface,就可以将View的数据绘制保存到Surface中,也就可以显示到屏幕上
    1. 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);
       }

这里方法虽然不多,但是逻辑有点绕,做一下解释:

    1. 是子容器调用的 makeChildSurface 方法,那子容器类就是 Task,父容器就是 DefaultTaskDisplayArea
    1. 执行 父容器 makeChildSurface方法的时候,又调用了getParent 获取父容器,执行 makeChildSurface,(眉头一皱,事情并不简单)这是开始递归了。
    1. 先不管递归,总之肯定的是 makeChildSurface方法不管怎么递归返回的还是一个SurfaceControl.Builder,然后调用setParent将DefaultTaskDisplayArea的Surface设置为其父节点。

这样一来,结果就是 :Task调用 父容器的makeChildSurface后,创建出了一个Surface,并且挂载到了父容器(DefaultTaskDisplayArea)的下面。

知道结果后,还是要弄清楚 递归方法是怎么创建 Task对应的Surface的

    1. 对于递归调用,最终要的就是找到递归结束的条件,当前这个递归结束的条件就是 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,

    1. 首先创建一个容器类型 SurfaceControl.Builder
    1. 设置name,当前场景是把Task的名字设置过去
    1. 然后设置一下父亲为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 来的:

    1. 先创建出一个WindowSurfaceController 对象 surfaceController
    1. 通过WindowStateAnimator::createSurfaceLocked 对 surfaceController 赋值,根据方法名猜测是创建了一个Surface
    1. 通过 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个重点:

    1. 设置窗口状态为 DRAW_PENDING
    1. 创建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有很多状态,以后会单独说,这里需要注意

  1. WindowState状态是保存在WindowStateAnimator中
  2. 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个点

  1. 第一个参数传递的字符串最终也会作为Surface的name
  2. 获取到WindowState对象,后面会设置为创建Surface的父节点
  3. 构建出一个Surface对象, 注意name和 父节点的设置。 另外可以知道也是通过makeSurface()方法构建的, 这个方法在 2.1小结看到是构建出一个"容器"类型的Surface。
  4. 将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");
    }
相关推荐
雨白20 分钟前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹2 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空4 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭4 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日5 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安5 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑5 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟9 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡11 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0011 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体