个人水平有限,可能存在纰漏或有描述问题。
涉及的点:
1.setContentview 中做了什么
2.super.onCreate 中干了什么
3.在什么时候创建了 decorview ,;decorview ,window,windowmanager 的关系。
4.在 xml 解析 view 时,通过 Factory2创建 view 有什么差异。
5.在resume 中 什么时候出发了 view 的绘制。
5.requestlayout 以及 invalidate的区别。
6.编舞者的作用,以及为啥要保证垂直同步信号和渲染节点的一致。
7.渲染的的2级缓存和3级缓存。
8.在绘制时,如何将 java 层的数据 传递到 c++层,并通过 surfaceflinger进行合成 ,并存放在BackBuffer再到展示到页面的过程。
执行流程
onCreate:
页面的渲染首先在 oncreate 方法中,依次通过 super.oncreate()方法完成 factory2的赋值操作,factory2 是后面 view,viewgroup,自定义 view 创建的核心类。
在 setcontentView()方法中设置了对应的 xml,在该方法内部会判断 Decorview 是否为空,如果为空,就会通过 phoneWindow 进行创建。同时实现 decorview 和 phonewindow相互持有。
(拓展:同时在 Androidx中 在该方法中会调用 initializeViewTreeOwners()方法,在该方法中会将LifecycleOwner、ViewModelStoreOwner等,同 decorview 进行绑定。因此在后面自定义 view 时,可以通过同 decorview 获取对应值,实现生命周期绑定,viewmodel 写入到 ViewModelStore等操作。)
在setContentView()方法后 会执行到 AppCompatDelegateImpl - setContentView方法:在该方法中会通过 LayoutInflater.from(mContext)方式,进行view树的创建操作。其中创建的过程都会通过mFactory2.onCreateView()方法进行创建;在创建时分为 view 、viewGroup、自定义 view 3种情况。其中 view 的创建会根据 view 名进行 new 出对应的 view。而 viewGroup 和自定义 view 会通过反射的形式进行创建。(自定义 view 和 ViewGroup 创建调用相同的方法,就是传递参数不同;自定义 view 没有携带前缀参数,在反射时直接通过 name 进行反射。viewGroup 是前缀+name 进行发射生成对象)。接下来就会将创建的 view 和 viewGroup 进行添加生成了对应的 view 树。
onResume:
在handleResumeActivity 执行 view 添加到 window,并执行绘制的流程.
在该方法中首先调用 windowmanager.addView()方法,在方法中最终会通过 WindowManagerGlobal.addview 方法进行添加;在该方法内部,会判断ViewRootImp 是否为空,如果为空就创建,并将当前线程作为UI 线程,并作为后期判断是否在 ui 线程的依据;接着将 view(DecorView),root,params 添加到对应的集合中;后面就会调用viewRootImpl.setView () 方法,在该方法内部会触发requestLayout方法,接着在requestLayout方法中 执行scheduleTraversals()方法,在该方法内部会通过 handler 设置异步栅栏。然后通过编舞者 设置回调;然后再回调结果中调用doTraversal方法。在该方法中,移除异步栅栏,然后执行performTraversals方法,该方法就会执行后续的performMeasure、performLayout、performDraw 方法进行测量排版和绘制功能。
编舞者:
编舞者(Choreographer):编舞者的主要作用就是调节作用,调节垂直同步信号和布局渲染的执行时间,保证两者节奏一致。避免出现丢帧出现。撕裂是因为没有设置2级缓存和3级缓存导致。
Choreographer 的作用
在 Android 中,Choreographer 是一个专门负责协调垂直同步信号(VSync)与 UI 渲染流程的调度器,它的主要功能是:
- 监听 VSync 信号
-
- VSync 是屏幕刷新信号(通常 16.6ms 一次,60Hz)。
Choreographer会在收到 VSync 信号时启动一次 UI 渲染调度。
- 调度三大绘制阶段
-
- 输入处理(Input)
- 动画计算(Animation)
- 视图绘制(Traversal → measure/layout/draw)
- 这些阶段会被安排在同一帧的合适时机执行,保证 UI 渲染和屏幕刷新同步。
- 避免丢帧
-
- 如果 UI 渲染完成时间超过 VSync 间隔,就会丢帧,产生卡顿。
Choreographer会尽量保证这些任务在 VSync 信号到来前完成,减少卡顿感。
接下来就是将布局图形绘制并展示页面:
1、surface 的创建:在performTraversals - relayoutWindow - 在windowmanagerservice 中创建 对应的 surface; java 中 surface:surface.mNativeObject = c++ surface.(其实就是 java 中的 surface.mNativeObject 持有 c++层 surface 的指针)
C++ surface :可以通过BufferQueueProducer 从 BufferQueue 中获取到 GraphicBuffer图形缓存区。
Surfaceflinger ->layer: 可以通过 BufferQueueConsumer从BufferQueue 获取图形缓存区(GraphicBuffer);
2.在绘制时 。surface.lock(dirty)时; 会先进行 surface 锁定。然后java 层的 surface.mNativeObject(c++层 surface)会通过BufferQueueProducer 获取到图形缓存区 GraphicBuffer。然后创建 BitmapInfo(包含区域的宽高,等信息) 。接着创建 SkBitmap 根据BimapInfo的数据信息 。view 图形最终绘制到 SkBimap 上。
3、canvas.draw 就是通过 nativeCanvas(SkCanvas) 将图形绘制到 SkBitmap 上。
4.Surface.unlockCanvasAndPost 方法,触发 surface.unlock 解锁。然后借助 BufferQueueProducer.queueBuffer将图形缓存区(GraphicBuffer)放入到 BufferQueue队列中。并通知到 Surfaceflinger有新的一帧数据,在下次垂直同步信号来时surfaceFlinger - layer 会借助BufferQueueConsumer从BufferQueue 中获取到缓存区(GraphicBuff)进行图形合成。然后存放到 Back Buffer 。在后续垂直同步信号到来时,将 frameBuffer 和 BackBuffer 进行替换,然后就展示到了页面。
两级缓存交互流程
[Frame Buffer] → 屏幕显示
[Back Buffer ] → GPU 绘制新帧
↓ 交换(Swap Buffers)
[Frame Buffer] ← 新内容显示
[Back Buffer ] ← 开始绘制下一帧
BufferQueue,BufferQueueProducer,BufferQueueConsumer,GraphicBuff
BufferQueue:
本质是一个生产者-消费者队列,用来在 生产者(Producer) 和 消费者(Consumer) 之间传递图形缓冲(GraphicBuffer)。
BufferQueueProducer:
生产者接口,供 应用层 Surface 使用。
负责:
- 向 BufferQueue 申请可写的缓冲区(dequeueBuffer)。
- 将绘制好的缓冲提交回队列(queueBuffer)。
BufferQueueConsumer
消费者接口,供 SurfaceFlinger(或其他消费方)使用。
负责:
-
- 从队列中取出已经绘制好的缓冲(acquireBuffer)。
- 用完后将缓冲释放回队列(releaseBuffer)。
GraphicBuffer:图形缓存区
一种跨进程共享的显存缓冲(底层基于 Gralloc)。
本质上是 GPU/硬件可直接访问的图像存储区域。