四、视图处理流程
这个部分牵涉到的内容比较多,主要来说分三块:视图绘制、视图渲染、视图合成,之前的文章也是花了好几个系列来写过。那么再好好完整地过一遍吧。
4.1 视图绘制渲染流程
1)布局加载
梳理布局加载首先需要了解视图层级关系,如下图所示:
Activity是系统可视化交互组件,四大组件都由AMS统一管理生命周期,事实上它的职责只是生命周期的管理,由设计模式的单一职责的原则,那势必需要将Activity和其上的视图View进行解耦,那么就引入Window的概念,它是应用端的抽象类,对于Activity来说,它的具体实现类是PhoneWindow,在Activity执行attach的时候,会创建了一个PhoneWindow对象。PhoneWindow作为装载根视图DecorView的顶级容器,Activity通过setContentView主要干两件事情:
- 调用PhoneWindow来创建DecorView,DecorView是整个ViewTree的rootView;
- 通过LayoutInflater.inflater,对xml布局做pull解析,创建对应的View对象,形成ViewTree,加载到DecorView的contentView部分。
2)应用程序窗口管理
在应用端,通过WindowManager对Window进行统一管理,WindowManager是一个接口类,继承自接口ViewManager,负责窗口的管理,例如:增、删、改。它的实现类是WindowManagerImpl,而具体操作实际上又会交给WindowManagerGlobal来处理,它是个单例,进程唯一。WindowManagerGlobal对每个窗口的DecorView和ViewRootImpl进行统一管理,同时与WMS进行binder call通信。而ViewRootImpl又是WindowManagerGlobal具体操作的执行者。
以addView为例,具体window是由WMS统一管理的,所以这里会进行binder IPC。
binder IPC:
IWindowSession: 应用程序通过Session与WMS通信,并且每个应用程序进程都会对应一个Session。
IWindow: 作为WMS主动与应用程序通信的client端,因为不同的Window是不同的client,因此它也被作为识别window的key。
3)WindowState创建
ViewRootImpl.setView()方法会向WMS请求添加一个Window,mWindowSession.addToDisplay()跨进程最终调用到了WMS.addWindow(): 这里会创建WindowState,它是WMS中描述应用程序窗口的对象。
4)与SurfaceFlinger建立连接(WMS与SurfaceFlinger通信)
WMS.addWindow()中WindowState通过attach方法建立建立一个SurfaceSession走windowAddedLocked()流程与SurfaceFlinger进行连接:
ComPoserService作为client 与 SurfaceFlinger server进行binder IPC , 获取到SurfaceFlinger创建的Client对象,它相当于是SurfaceFlinger内部对应用程序客户端的封装对象,而Client与SurfaceComposerClient又互为binder ipc的两端,SurfaceComposerClient为client端,Client为server端。
5)创建Surface(WMS分别与应用程序和SurfaceFlinger通信)
Surface是真正UI视图的载体和处理者。
ViewRootImpl内会new一个Surface,ViewRootImpl 、WindowState与Surface是一一对应关系。
创建Surface主要走的是ViewRootImpl setView操作的requestLayout流程中的relayoutWindow部分:这个过程实际上就是在SurfaceFlinger创建Layer,对应在客户端通过SurfaceControl在native层创建Surface并copyForm返回给ViewRootImpl内java层的Surface,为什么这么麻烦?因为Surface主要负责调用绘制引擎执行渲染视图的操作,这部分工作在native效果更高。并且Android4.0之后的硬件加速绘制,渲染过程是又native进程RenderThread来负责的。
三者关系是:
- View是视图的素材;
- WindowState是窗口实体对象,WMS会通过z-order计算来决定窗口的层级关系,为SurfaceFlinger合成Layer提供层级依据。
- Surface是View的载体和处理者。它会调用绘制引擎处理View,然后申请一块buffer,存入绘制素材,传递给SurfaceFlinger。
6)scheduleTraversals
添加视图会走ViewRootImpl的setView方法,这个方法有两个核心,一个与WMS通信创建WindowState,一个是执行scheduleTraversals开始进入绘制流程,这个流程东西比较多,主要分为以下几个部分:
- 消息屏障
- Choreographer请求vsync信号
- measure
- layout
- draw
下面一一来梳理。
7)消息屏障
Handler中的Message可以分为两类:同步消息、异步消息。通过MessageQueue.postSyncBarrier(是@hide方法)函数来设置同步屏障,当设置了同步屏障之后,Looper循环中将会忽略所有的同步消息,返回异步消息。换句话说,同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。
在scheduleTraversals中postCallback最终发送的消息通过msg.setAsynchronous(true);设置为了异步消息,它的目的其实就是提高UI任务的优先级,让perfromTraversal提前于所有同步消息执行。
8)Choreographer请求vsync信号
这里有两点需要先说明:
- 流畅度概念 人肉眼对画面流畅感的最低要求是:显卡合成帧速率为60fps,即16.6ms绘1帧的要求。
- vsync Android16.6ms发出一次vsync信号,类似定时中断,同步整个绘制、渲染、合成流程。但是原则是按需请求,主要请求与接收vsync信号的类是:Choreographer(视图绘制)、SurfaceFlinger(视图合成)。
在Android4.1之后增加了Choreographer机制,用于同Vsync机制配合,统一动画、输入和绘制时机。在scheduleTraversals会post一个CALLBACK_TRAVERSAL类型的callback,请求vysnc信号,最终执行doFrame回调,来执行performTraversals。performTraversals流程最主要的三个工作就是测量、布局与绘制。
9)performMeasure
先来看测量。在measure中,模式和尺寸通过MeasureSpec来封装。MeasureSpec代表一个32位int值,高2位代表SpecMode,低30位代表SepcSize. 这样的打包方式好处是避免过多的对象内存分配。
SpecMode:测量模式
模式 | 描述 |
---|---|
UNSPECIFIED | 父容器不作限制,一般用于系统内部 |
EXACTLY | 精确模式,大小为SpecSize,对应LayoutParams中的match_parent或者具体数值 |
AT_MOST | 最大模式,大小不能大于SpecSize,对应于LayoutParams中的warp_content |
SpecSize:对应某种测量模式下的尺寸大小
这里DecorView的MeasureSpec由窗口尺寸和其自身LayoutParams共同决定,普通View其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。其中,LayoutParams类是用于子视图向父视图传达自己尺寸意愿的一个参数包,包含了Layout的高、宽信息。LayoutParams在LayoutInflater.inflater过程中与View一起被解析成对象,保存在WindowManagerGlobal集合中。
measure过程:
如果是强制测绘或者没有缓存,执行onMeasure(),否者从缓存中取出数据。View的onMeasure()就是解析宽高的MeasureSpec分别与SuggestedMinimumWidth共同决定其:mMeasuredWidth、mMeasuredHeight。
SuggestedMinimumWidth逻辑:如果View没有设置背景,那么返回mMinWidth的值,否则返回mMinWidth和背景最小宽度的最大值。而ViewGroup还会执行measureChild,将measure任务传递到每个子View处理。
10)performLayout
布局流程相对比较简单,layout的作用是ViewGroup用来确定子view的位置,当ViewGroup的位置被确定之后,它在onLayout中会遍历所有子view, 最终通过setFrame来设置每个子View的左上右下。
11)performDraw
视图绘制过程,这里分为软件绘制和硬件加速:
软件绘制由CPU绘制,CPU的特点是擅长串行的复杂逻辑运算,而硬件加速由GPU绘制,GPU擅长大量并行的相同简单逻辑工作。而视图的绘制工作明显属于后者,因此GPU效率更高,4.0之后,Android默认启用硬件加速。
软件绘制走的drawSoftware方法:
- 调用lockCanvas, 实际执行逻辑在native层对应的nativeLockCanvas中,实现:构造SkiaCanvas;向SurfaceFlinger dequeue一块GraphicBuffer. 前者用于绘制,后者用于存放并传输UI视图元素。
- View.draw(canvas),通过构造的Canvas绘制视图,最终UI元素保存到申请到的GraphicBuffer,绘制API对应是底层Skia库。
- 调用unlockCanvasAndPost 实现:queueBuffer给SurfaceFlinger 并且通知其消费。
整个绘制与渲染过程都是在UI Thread完成的,因为现在Android基本不走软件绘制了,所以具体细节不详细分析。
而硬件加速绘制走ThreadedRenderer的draw方法
整个硬件加速的绘制与渲染过程简述如下:
UIThread:
ThreadedRenderer的draw方法实际上就是构建DrawOp树(里面封装OpenGL渲染命令)。在Android硬件加速框架中,View视图被抽象成RenderNode节点,View中的绘制都会被抽象成一个个DrawOp(DisplayListOp),整个绘制过程由CPU完成,递归绘制完成后,构建出DisplayList,数据结构如下:
RenderThread:
UIThread将DisplayList同步到RenderThread,RenderThread有一个大loop,绘制操作都以RenderTask的形式post到RenderThread中处理,RenderThread负责渲染,由GPU来处理。渲染核心逻辑:
- OpenGLPipeline::getFrame(): 先通过dequeueBuffer申请一块GraphicBuffer,
- OpenGLPipeline::draw: 将DisplayList递归优化并转为标准openGL命令,提交给GPU渲染,得到的数据放入申请的GraphicBuffer中。
- OpenGLPipeline::swapBuffers 通过queueBuffer将GraphicBuffer投入BufferQueue ,同时触发invalidate-sf-vsync请求,通知SurfaceFlinger处理。
4.2 视图合成与送显流程
- INVALIDATE:接收invalidate-sf-vsync请求,acquireBuffer更新脏区域、发送refresh-sf-vsync请求。
- REFRESH:接收refresh-sf-vsync请求,按z-order计算可见区域,合成视图并做栅格化处理,通过FrameBuffer送显。
视图处理整体流程:
到这里,从应用安装,到点击Laucher应用图标,最后显示主页视图的整个流程就简单梳理完了。
可以参看之前的相关文章:
Android图形系统(一)-Window加载视图过程
Android图形系统(二)-DecorView布局加载流程
Android图形系统(三)-View绘制流程
Android图形系统(四)-Activity、Window、View关系总结
Android图形系统(五)-Surface图形系统概览
Android图形系统(六)-app与SurfaceFlinger服务连接过程
Android图形系统(七)-app请求SurfaceFlinger创建Surface过程
Android图形系统(八)-app与SurfaceFlinger共享UI元数据过程
Android图形系统(九)-View、Canvas与Surface的关系
Android图形系统(十)-SurfaceFlinger启动及图层合成送显过程
Android图形系统(十一)-Choreographer
Android图形系统(十二)-流畅度概念
Android图形系统(十三)-Vsync信号处理
Android9.0 硬件加速(一)-开篇
Android9.0 硬件加速(二)-RenderThread线程的启动
Android9.0 硬件加速(三)-绑定Surface到RenderThread
Android9.0 硬件加速(四)-UI Thread绘制过程
Android9.0 硬件加速(五) -RenderThread渲染过程
Android WMS(一)-窗口管理
Android WMS(二)-Surface管理
从systrace看app冷启动过程(一)-应用程序启动
从systrace看app冷启动过程(二)-首帧的绘制与渲染
从systrace看app冷启动过程(三)-首帧的合成与送显