应用启动流程梳理(四)-视图处理流程

四、视图处理流程

这个部分牵涉到的内容比较多,主要来说分三块:视图绘制、视图渲染、视图合成,之前的文章也是花了好几个系列来写过。那么再好好完整地过一遍吧。

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端。

image.png

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,数据结构如下:

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冷启动过程(三)-首帧的合成与送显

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342