App 端:
- apk: android 是一个多进程的操作系统,一个系统可以有多个 apk(进程) 运行(一个 apk 其实也可以有多个进程,这里不展开说了)。对应图中的 App1、App2。
- activity: 一个 apk 里面有多个 activity。对应图中的 Activity1、Activity2。
- window: 一个 activity 里面可以有多个 window,但是必须要有一个主窗口。图中这个 main window 不是官方的概念,是我的理解。例如说正常 startActivity 都会启动一个界面,这个就是 main window,一般 main window 都是全屏的,但是也可是悬浮的窗口。在代码里面一个 main window 对应的是 PhoneWindow,它是抽象类 Window 在手机上的策略的实现(以前还有 TabletWindow 的,但是现在好像没了)。一般来说一个 activity 也就一个 PhoneWindow(main window 那个) ,但是如果你弹 Dialog 出来的话,那么一个 Dialog 也包含了一个 PhoneWIndow。所以一个 activity 可以有多个 window。
- DecorView: 前面有介绍,它是顶层 View,是所有 View 的 Parent,是 andrid view 框架的起始部分。一个 PhoneWindow 都有一个 DecorView。但是 PopupMenu 比较特殊,它没有 PhoneWindow,取而代之的是自己的 PopupWindow,这个类没有继承 Window,而是自己实现了一些逻辑,在 PopupWindow 里面也有一个 DecorView(PopupDecorView 和 DecorView 是类似的功能)。
- ViewRootImp: 看名字就知道 ViewRootImp 很重要,它有2个主要作用: (1). 实现 android 的 View 系统的逻辑,发起布局、绘制等操作;(2). 连接 应用窗口 和 服务端 WMS 的桥梁。它里面有一个内部类 H 实现了 IWindow 接口(Bn端)。对应就是 WMS 里面 WindowState 当中的 mClient(Bp端)。它是应用把自己的 Window 挂载到 WMS 的时候创建的,一个 Window 有一个 ViewRootImp,严格来说它不隶属于 DecorView,WindowMangerGlobal 保存着所有的 ViewRootImp,只不过 DecorView 也持有了 ViewRootImp ,而且会调用它的一些接口而已。
- Surface: 熟悉 GUI 框架的应该对这个名词不陌生,在 android 里面 Surface 和其他 GUI 的类似,提供一块内存(Buffer)给应用绘制图像(为什么不叫画布,因为在 android 里面画布叫 Canvas,相比 Surface,Canvas 提供的是绘制图形的操作,例如说画线、画点),然后再把绘制好图像的 Buffer 发送给 SurfaceFlinger(SF) 进行合成显示。ViewRootImp 持有 Surface 的引用,Surface 是在 SF 端创建的,通过 Binder 传递给应用端(ViewRootImp)使用。所以说 ViewRootImp 可以发起绘制操作,因为它有 Surface。SurfaceControl 是对 Surface 的封装,提供一些访问 Surface 的操作。可以说一个 ViewRootImp 对应一个 Surface。
- BufferQueue: 前面的官方里面有介绍这个,它提供一个生产者、消费者模型:生产者(BufferQueueProducer)生产图像,消费者(BufferQueueConsumer)消费图像。BufferQueue 提供一组 Buffer (三重缓冲)在这个模型里面流转:生产者需要绘制图像了就请求 Buffer(dequeueBuffer),拿到后绘制图像(生产图像),然后再把这块 Buffer 还给 BufferQueue(queueBuffer),BufferQueue 通知消费者有新图像 Buffer 来了,消费者就会取这块 Buffer(acquireBuffer)拿去使用(例如说 SF 做显示)。我们这里生产者就是 ViewRootImp,消费者是 SF 里面的 Layer。android 提供这套模型的目的是:生产者、消费者模型不仅仅只适用于系统图形系统,例如编解码也可以适用,例如说录像的时候,camera 是生产者,编码器就是消费者。BufferQueue 的生产者,消费者的接口都是 Binder 接口,可以跨进程的。顺带说一句上面流转的 Buffer 都是用共享内存实现的,进程间来回传递的是文件句柄,不需要 copy 数据。一个 Surface 会有一个 IGraphicBufferProducer 接口的引用(Bp端),和它对应的是 SF 里面 Layer 的 BufferQueueProducer(Bn端) 。BufferQueue 是 SF 里面的 Layer 创建的,传了一个 IGraphicBufferProducer 给 Surface,ViewRootImp 通过 Surface 访问 IGraphicBufferProducer 接口就可以申请绘制需要的 buffer,和把 buffer 送给显示。
- View: View 可以是 android GUI 的基本元素单位,它是有层级的,层层嵌套组成图形界面。前面说了 DecorView 是顶级 View,应用创建的 View 都是挂载在 DecorView 下面的(其实是 DecorView 下面的某个View)。也就是说一个 DecorView 可以对应多个 View。但是前面说了送给显示的是 ViewRootImp 下面的 Surface,View 绘制的 Buffer 是什么呢? 有2个选择,可以使用 ViewRootImp 的 Surface 的 Buffer,这个是 View 的常规操作,每个 View 会被限制在自己的区域内绘制(measure 的时候确定的 View 大小),影响不到其他 View(因为大家用的都是同一块 Buffer)。也可以单独给 View 创建 Buffer 使用,最后 ViewRootImp 绘制操作的时候,会把所有 View 单独创建的 Buffer draw 到 Surface 的 Buffer 上(这个有点像 SF Layer 的合成操作)。然后再把 Buffer 送给 SF 合成显示。
- WindowManagerGlobal: 这是一个单例模式,也就是说一个进程就只有一个。里面有一个 WindowSession,也是单例的,这个 WindowSession 是应用连接 WMS 的接口,通过这个接口可以访问到 WMS (这个接口主要是对内的,对外的是 WindowManager)。 对应 WMS 中有一个数组 mSessions,保存了所有客户端的 WindowSession 连接(应用端的 Session 是 Bp端,WMS 里面的是 Bn端)。本进程中所有 ViewRootImp 使用的 WindowSession 都是 WindowManagerGlobal 里面这个。
- SurfaceView: 前面介绍的,一个 PhoneWindow 有一个 DecorView,一个 DecorView 有一个 ViewRootImp,一个 ViewRootImp 有一个 Surface(适用于 activity 的 main window、Dialog、PopupMenu)。但是看图会发现有一个特殊,那就是 SurfaceView。SurfaceView 没有 PhoneWindow,也没有 ViewRootImp, 甚至都没有使用全局的 sWindowSession。它是自己直接打开了和 SF 的连接创建了自己的 SurfaceSession(后面会介绍,WindowSession 会创建 SurfaceSession 连接 SF),然后通过 SurfaceSession 创建了自己的 Surface。所以官方说 SurfaceView 可以用单独的线程渲染,因为它基本脱离了 android 的 View 系统,给应用提供了可以绘制的 Buffer(有自己的 Surface 就可以 dequeue/queueBuffer),让应用自由发挥。而其他的 View 的绘制必须在主线程(也就是前面提到的 UIThread),因为后面会介绍 ViewRootImpl 的绘制操作是在 UIThread 发起的,而所有的 ChildView 的绘制都是在 ViewRootImp 的绘制中调用的。上面之所以说 SurfaceView 没有完全脱离 android View 的系统,是因为虽然绘制可以应用自己操控,但是 SurfaceView 还是受到 View 的区域大小限制(创建的 Surface 大小是 SurfaceView 区域的大小),而且 SurfaceView 也是挂载在 View tree 上的。
WindowManagerService 端:
- WindowSession: 前面已经说过这个是对应用的 IWindowSession,普通 Window 用的是进程全局的 WindowSession,也就是一个 Apk 一个 WindowSession(这里假设 Apk 是单进程的)。 WMS 里面的 WindowSession 会去打开和 SF 的连接,创建一个 SurfaceSession,而一个 SurfaceSession 包含了一个 SurfaceComposerClient。SurfaceComposerClient 是 libgui 的一个 native 接口,SF 的 Client 类实现了这个接口。一个 SurfaceComposerClient 对应 SF 里面一个 Client 实例。在我们启动流程分析里面,这个 Client 主要是提供创建 Surface 的接口。前面说 SurfaceView 有自己的 SufraceSession,就是有自己的 SurfaceComposerClient,所以它可以创建自己的 Surface。
- WindowState: 可以说应用端一个 Window 在 WMS 就对应一个 WindowState。在 WindowState 中主要有一个 mClient,对应是应用端 ViewRootImp 的内部类 H(IWindow)。然后 SurfaceControl 是在创建 Surface 的时候,WMS 这边也存了一个 SurfaceControl,后续一些功能会需要操作到。这个 SurfaceControl 和应用端的 SurfaceControl 是对应的。
SurfaceFlinger 端:
- Client: 前面有介绍,这个就是和 SurfaceComposeClient 一一对应的(上层必须会有 SurfaceSession)。因为应用普通 Window 的 WindowSession 是全局的,也就导致了没有 SurfaceView 的应用就只有 一个 SurfaceSession,所以一个 SurfaceSession 可以创建多个 Surface。
- Layer: 这个是 SF 中的基本单位,应用端(WMS 端)通过 SurfaceComposeClient(SurfaceSessison)调用创建 Surface 的接口,在 SF 里面本质是创建一个 Layer。一个 Layer 会创建一个 BufferQueue(BufferQueueCore),一个 BufferQueue 会有一个 BufferQueueProducer 和 BufferQueueConsumer。然后返回的时候会把 BufferQueueProducer 返回回去(BufferQueueProducer 是可以 Binder 传递的,反而 Surface 不行)。然后应用这边再通过 BufferQueueProducer 重新 new 了一个 Surface。所以 Surface 其实只是一个壳,Surface 提供 Buffer 的功能本质是上 BufferQueueProducer 提供的。SF 合成的时候,会遍历每个 Layer,能用 HWC 合成的(这个策略由具体的硬件决定,例如说某些硬件不支持合成带 aphla 的 Layer;或者是某些场景下 Layer 数量超过 HWC 支持的数量了;或者上层强制标记为 GPU 合成了,Settings 的开发者选项”强制GPU合成”,就是把所有 Layer 标记为 GPU 合成),就跳过,否则就标记为需要 GPU 合成;然后把需要 GPU 合成的 Layer 全部用 OpenGL draw 到另外一个单独的 Layer 上,最后把这个绘制好的 Layer 送给 HWC。所以 GPU 合成(在 SF 代码里面叫 Client 方式)是指用 OpenGL(GPU)把 Layer 绘制(混合)到一个单独的 Layer,再把这个 Layer 送给 HWC。而 HWC 合成是指省去了 SF 用 OpenGL 混合 Layer 的过程,直接把 Layer 送给 HWC,由 HWC 混合显示到屏幕上。所以每一帧显示,如果是 GPU 合成的话,需要额外的 GPU 运算能力,同时增加 ddr 带宽访问量(每个 Layer 需要一次 读+写 Layer Buffer 的 ddr 数据量);导致功耗上升和 ddr 带宽需求上升。所以一些需要用到 GPU 合成的场景可能就会引发性能问题。
经过上面的解说,我们可以再精简总结一下:
- 一个 activity 可以有多个 window
- 一个 window 对应一个 DecorView,一个 DecorView 下面可以挂载多个 View;一个 window 对应一个 ViewRootImpl,一个 ViewRootImp 对应一个 Surface。一个 window 对应一个 WindowState。
- 一个 Surface 对应一个 Layer;一个 Layer 对应一个 BufferQueue