第三板块:Android 图形渲染与窗口体系 | 第十四篇:View 绘制体系与 RenderThread 异步渲染
所属板块:第三板块 --- Android 图形渲染与窗口体系
前置知识:第十三篇中的 SurfaceFlinger 合成机制、VSYNC 信号、BufferQueue 原理
本篇定位 :这是 Android 应用层渲染的终极奥秘 。我们将彻底拆解 View 的 Measure/Layout/Draw 三大流程 、DisplayList 的录制与回放 、RenderThread 的异步渲染机制 、UI 线程与 RenderThread 的同步屏障(Sync Barrier) 。我们将深入 Android Framework 的 UI 渲染源码 ,揭示为何 Android 能够实现 60/90/120 FPS 的流畅交互 ,以及 UI 线程卡顿 的根本原因。全程无 UI 优化技巧、无卡顿排查指南,仅保留 Android 图形系统的底层定义与渲染规范。
1. 核心结论先行(Thesis Statement)
Android 的 View 渲染是一个多线程流水线。
- UI 线程(Main Thread)的本质 :指挥官 。它负责执行 Measure、Layout、Draw(软件绘制)或 DisplayList 录制(硬件加速)。它不负责真正的像素填充。
- RenderThread 的本质 :执行者 。它是一个独立的 Binder 线程 ,运行在应用进程中,专门负责将 DisplayList 转换为 GPU 指令 ,并调用 OpenGL ES / Vulkan 进行光栅化。
- DisplayList 的本质 :绘图命令的缓存 。UI 线程将 View 的绘制操作(如
drawRect,drawText)记录为一个命令列表,RenderThread 读取并执行这些命令。 - 同步屏障(Sync Barrier):确保 UI 线程和 RenderThread 在正确的时间点进行数据交换,防止竞态条件。
2. View 渲染的全景流水线
2.1 从 Choreographer 到屏幕的旅程
Display SurfaceFlinger GPU RenderThread UI 线程 (Main) Choreographer VSYNC 信号 Display SurfaceFlinger GPU RenderThread UI 线程 (Main) Choreographer VSYNC 信号 #mermaid-svg-sj7zb2bntro0Qj0A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sj7zb2bntro0Qj0A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sj7zb2bntro0Qj0A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sj7zb2bntro0Qj0A .error-icon{fill:#552222;}#mermaid-svg-sj7zb2bntro0Qj0A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sj7zb2bntro0Qj0A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sj7zb2bntro0Qj0A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sj7zb2bntro0Qj0A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sj7zb2bntro0Qj0A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sj7zb2bntro0Qj0A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sj7zb2bntro0Qj0A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sj7zb2bntro0Qj0A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sj7zb2bntro0Qj0A .marker.cross{stroke:#333333;}#mermaid-svg-sj7zb2bntro0Qj0A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sj7zb2bntro0Qj0A p{margin:0;}#mermaid-svg-sj7zb2bntro0Qj0A .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sj7zb2bntro0Qj0A text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-sj7zb2bntro0Qj0A .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-sj7zb2bntro0Qj0A .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-sj7zb2bntro0Qj0A .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-sj7zb2bntro0Qj0A .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-sj7zb2bntro0Qj0A #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-sj7zb2bntro0Qj0A .sequenceNumber{fill:white;}#mermaid-svg-sj7zb2bntro0Qj0A #sequencenumber{fill:#333;}#mermaid-svg-sj7zb2bntro0Qj0A #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-sj7zb2bntro0Qj0A .messageText{fill:#333;stroke:none;}#mermaid-svg-sj7zb2bntro0Qj0A .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sj7zb2bntro0Qj0A .labelText,#mermaid-svg-sj7zb2bntro0Qj0A .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-sj7zb2bntro0Qj0A .loopText,#mermaid-svg-sj7zb2bntro0Qj0A .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-sj7zb2bntro0Qj0A .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-sj7zb2bntro0Qj0A .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-sj7zb2bntro0Qj0A .noteText,#mermaid-svg-sj7zb2bntro0Qj0A .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-sj7zb2bntro0Qj0A .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sj7zb2bntro0Qj0A .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sj7zb2bntro0Qj0A .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sj7zb2bntro0Qj0A .actorPopupMenu{position:absolute;}#mermaid-svg-sj7zb2bntro0Qj0A .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-sj7zb2bntro0Qj0A .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sj7zb2bntro0Qj0A .actor-man circle,#mermaid-svg-sj7zb2bntro0Qj0A line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-sj7zb2bntro0Qj0A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} doFrame()执行 CALLBACK_INPUT (输入处理)处理触摸事件执行 CALLBACK_ANIMATION (动画)计算动画值执行 CALLBACK_TRAVERSAL (遍历)measure() ->> layout() ->> draw()录制 DisplayList (硬件加速)提交 DisplayList (同步屏障)执行 GPU 指令 (glDrawElements)光栅化、着色queueBuffer() (提交 Buffer)合成 (HWC/GPU)Present Frame
2.2 关键阶段解析
| 阶段 | 执行线程 | 学术定义 |
|---|---|---|
| Input | UI 线程 | 处理用户输入(触摸、按键),触发事件回调。 |
| Animation | UI 线程 | 计算动画的当前值(Translation, Alpha, Scale)。 |
| Traversal | UI 线程 | 执行 View 树的 Measure、Layout、Draw。 |
| DisplayList Recording | UI 线程 | 将 Draw 操作转换为 DisplayList 命令。 |
| RenderThread Execution | RenderThread | 解析 DisplayList,调用 GPU API。 |
| Swap Buffers | RenderThread | 交换前后台 Buffer,通知 SurfaceFlinger。 |
3. View 的三大绘制流程(Measure/Layout/Draw)
3.1 Measure 流程(尺寸测量)
Measure 是一个自顶向下的递归过程。
学术定义:
- MeasureSpec : 父 View 对子 View 的约束。包含 Mode 和 Size 。
- EXACTLY : 父 View 决定了子 View 的确切大小(如
match_parent或固定值)。 - AT_MOST : 子 View 可以达到的最大尺寸(如
wrap_content)。 - UNSPECIFIED: 父 View 对子 View 没有约束(如 ScrollView 中的子 View)。
- EXACTLY : 父 View 决定了子 View 的确切大小(如
- onMeasure(): View 根据自身内容和 MeasureSpec 计算期望尺寸。
源码解析:
java
// View.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)
);
}
3.2 Layout 流程(位置确定)
Layout 是一个递归放置的过程。
学术定义:
- onLayout(): ViewGroup 负责确定其子 View 的位置(left, top, right, bottom)。
- RelativeLayout 的噩梦 : RelativeLayout 可能需要进行两次 Measure 才能确定位置,导致性能下降。
3.3 Draw 流程(绘制指令)
Draw 分为两种模式:软件绘制 和硬件加速。
| 模式 | 执行方式 | 性能 |
|---|---|---|
| 软件绘制 | CPU 直接在 Bitmap 上绘制像素 | 慢,占用 CPU,易卡顿 |
| 硬件加速 | CPU 录制命令,GPU 执行渲染 | 快,GPU 并行处理,流畅 |
硬件加速下的 Draw:
java
// View.java (硬件加速)
public void draw(Canvas canvas) {
// 1. 绘制背景
background.draw(canvas);
// 2. 绘制自己
onDraw(canvas);
// 3. 绘制子 View
dispatchDraw(canvas);
// 4. 绘制装饰
onDrawForeground(canvas);
}
4. DisplayList 与 RenderNode
4.1 DisplayList 的录制
在硬件加速下,Canvas 不再是直接绘制像素,而是录制命令。
java
// DisplayListCanvas.java
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
// 将命令添加到 DisplayList
mDisplayList.addDrawRect(left, top, right, bottom, paint);
}
4.2 RenderNode 的层级结构
每个 View 对应一个 RenderNode。RenderNode 包含 DisplayList 和属性(位置、透明度、变换矩阵)。
#mermaid-svg-7TDmfLMtAokVLyk4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7TDmfLMtAokVLyk4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7TDmfLMtAokVLyk4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7TDmfLMtAokVLyk4 .error-icon{fill:#552222;}#mermaid-svg-7TDmfLMtAokVLyk4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7TDmfLMtAokVLyk4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7TDmfLMtAokVLyk4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7TDmfLMtAokVLyk4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7TDmfLMtAokVLyk4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7TDmfLMtAokVLyk4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7TDmfLMtAokVLyk4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7TDmfLMtAokVLyk4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7TDmfLMtAokVLyk4 .marker.cross{stroke:#333333;}#mermaid-svg-7TDmfLMtAokVLyk4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7TDmfLMtAokVLyk4 p{margin:0;}#mermaid-svg-7TDmfLMtAokVLyk4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7TDmfLMtAokVLyk4 .cluster-label text{fill:#333;}#mermaid-svg-7TDmfLMtAokVLyk4 .cluster-label span{color:#333;}#mermaid-svg-7TDmfLMtAokVLyk4 .cluster-label span p{background-color:transparent;}#mermaid-svg-7TDmfLMtAokVLyk4 .label text,#mermaid-svg-7TDmfLMtAokVLyk4 span{fill:#333;color:#333;}#mermaid-svg-7TDmfLMtAokVLyk4 .node rect,#mermaid-svg-7TDmfLMtAokVLyk4 .node circle,#mermaid-svg-7TDmfLMtAokVLyk4 .node ellipse,#mermaid-svg-7TDmfLMtAokVLyk4 .node polygon,#mermaid-svg-7TDmfLMtAokVLyk4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7TDmfLMtAokVLyk4 .rough-node .label text,#mermaid-svg-7TDmfLMtAokVLyk4 .node .label text,#mermaid-svg-7TDmfLMtAokVLyk4 .image-shape .label,#mermaid-svg-7TDmfLMtAokVLyk4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-7TDmfLMtAokVLyk4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7TDmfLMtAokVLyk4 .rough-node .label,#mermaid-svg-7TDmfLMtAokVLyk4 .node .label,#mermaid-svg-7TDmfLMtAokVLyk4 .image-shape .label,#mermaid-svg-7TDmfLMtAokVLyk4 .icon-shape .label{text-align:center;}#mermaid-svg-7TDmfLMtAokVLyk4 .node.clickable{cursor:pointer;}#mermaid-svg-7TDmfLMtAokVLyk4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7TDmfLMtAokVLyk4 .arrowheadPath{fill:#333333;}#mermaid-svg-7TDmfLMtAokVLyk4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7TDmfLMtAokVLyk4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7TDmfLMtAokVLyk4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7TDmfLMtAokVLyk4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7TDmfLMtAokVLyk4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7TDmfLMtAokVLyk4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7TDmfLMtAokVLyk4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7TDmfLMtAokVLyk4 .cluster text{fill:#333;}#mermaid-svg-7TDmfLMtAokVLyk4 .cluster span{color:#333;}#mermaid-svg-7TDmfLMtAokVLyk4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7TDmfLMtAokVLyk4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7TDmfLMtAokVLyk4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-7TDmfLMtAokVLyk4 .icon-shape,#mermaid-svg-7TDmfLMtAokVLyk4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7TDmfLMtAokVLyk4 .icon-shape p,#mermaid-svg-7TDmfLMtAokVLyk4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7TDmfLMtAokVLyk4 .icon-shape .label rect,#mermaid-svg-7TDmfLMtAokVLyk4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7TDmfLMtAokVLyk4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7TDmfLMtAokVLyk4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7TDmfLMtAokVLyk4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Root RenderNode (DecorView)
RenderNode (LinearLayout)
RenderNode (TextView)
RenderNode (ImageView)
学术定义:
- RenderNode: 一个可渲染的节点,包含几何信息和绘制命令。
- DisplayList: RenderNode 中的绘图命令列表。
- Property : RenderNode 的属性(如
translationX,alpha),修改属性不会触发重绘,只会触发重新合成。
5. RenderThread 的异步渲染机制
5.1 RenderThread 的启动
RenderThread 在应用进程启动时创建,是一个 Binder 线程。
java
// ThreadedRenderer.java
void initialize(Surface surface) {
// 创建 RenderThread
nInitialize(mNativeProxy, surface);
}
5.2 同步屏障(Sync Barrier)
UI 线程和 RenderThread 需要同步,防止 UI 线程修改正在渲染的数据。
学术定义:
- FrameInfo: 包含帧的元数据(帧号、时间戳、UI 线程耗时、RenderThread 耗时)。
- Sync Barrier: 在 UI 线程提交 DisplayList 后,设置一个屏障,直到 RenderThread 消费完数据。
#mermaid-svg-E8I5Vqttont6RTGn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-E8I5Vqttont6RTGn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-E8I5Vqttont6RTGn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-E8I5Vqttont6RTGn .error-icon{fill:#552222;}#mermaid-svg-E8I5Vqttont6RTGn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-E8I5Vqttont6RTGn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-E8I5Vqttont6RTGn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-E8I5Vqttont6RTGn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-E8I5Vqttont6RTGn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-E8I5Vqttont6RTGn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-E8I5Vqttont6RTGn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-E8I5Vqttont6RTGn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-E8I5Vqttont6RTGn .marker.cross{stroke:#333333;}#mermaid-svg-E8I5Vqttont6RTGn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-E8I5Vqttont6RTGn p{margin:0;}#mermaid-svg-E8I5Vqttont6RTGn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-E8I5Vqttont6RTGn .cluster-label text{fill:#333;}#mermaid-svg-E8I5Vqttont6RTGn .cluster-label span{color:#333;}#mermaid-svg-E8I5Vqttont6RTGn .cluster-label span p{background-color:transparent;}#mermaid-svg-E8I5Vqttont6RTGn .label text,#mermaid-svg-E8I5Vqttont6RTGn span{fill:#333;color:#333;}#mermaid-svg-E8I5Vqttont6RTGn .node rect,#mermaid-svg-E8I5Vqttont6RTGn .node circle,#mermaid-svg-E8I5Vqttont6RTGn .node ellipse,#mermaid-svg-E8I5Vqttont6RTGn .node polygon,#mermaid-svg-E8I5Vqttont6RTGn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-E8I5Vqttont6RTGn .rough-node .label text,#mermaid-svg-E8I5Vqttont6RTGn .node .label text,#mermaid-svg-E8I5Vqttont6RTGn .image-shape .label,#mermaid-svg-E8I5Vqttont6RTGn .icon-shape .label{text-anchor:middle;}#mermaid-svg-E8I5Vqttont6RTGn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-E8I5Vqttont6RTGn .rough-node .label,#mermaid-svg-E8I5Vqttont6RTGn .node .label,#mermaid-svg-E8I5Vqttont6RTGn .image-shape .label,#mermaid-svg-E8I5Vqttont6RTGn .icon-shape .label{text-align:center;}#mermaid-svg-E8I5Vqttont6RTGn .node.clickable{cursor:pointer;}#mermaid-svg-E8I5Vqttont6RTGn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-E8I5Vqttont6RTGn .arrowheadPath{fill:#333333;}#mermaid-svg-E8I5Vqttont6RTGn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-E8I5Vqttont6RTGn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-E8I5Vqttont6RTGn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E8I5Vqttont6RTGn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-E8I5Vqttont6RTGn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E8I5Vqttont6RTGn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-E8I5Vqttont6RTGn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-E8I5Vqttont6RTGn .cluster text{fill:#333;}#mermaid-svg-E8I5Vqttont6RTGn .cluster span{color:#333;}#mermaid-svg-E8I5Vqttont6RTGn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-E8I5Vqttont6RTGn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-E8I5Vqttont6RTGn rect.text{fill:none;stroke-width:0;}#mermaid-svg-E8I5Vqttont6RTGn .icon-shape,#mermaid-svg-E8I5Vqttont6RTGn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E8I5Vqttont6RTGn .icon-shape p,#mermaid-svg-E8I5Vqttont6RTGn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-E8I5Vqttont6RTGn .icon-shape .label rect,#mermaid-svg-E8I5Vqttont6RTGn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E8I5Vqttont6RTGn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-E8I5Vqttont6RTGn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-E8I5Vqttont6RTGn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 提交 DisplayList
等待
渲染完成
下一帧
UI 线程: 完成 Draw
同步屏障
RenderThread: 开始渲染
移除屏障
5.3 RenderThread 的工作流程
java
// RenderThread.cpp
void RenderThread::threadLoop() {
while (true) {
// 1. 等待 UI 线程的信号
waitForWork();
// 2. 处理 Frame
processFrame();
// 3. 执行 GPU 命令
executeDrawCommands();
// 4. 交换 Buffer
swapBuffers();
}
}
6. 硬件加速的渲染管线
6.1 OpenGL ES 管线
RenderThread 使用 OpenGL ES 进行渲染。
| 阶段 | 操作 | 学术定义 |
|---|---|---|
| 顶点处理 | 将 View 的坐标转换为屏幕坐标 | Vertex Shader |
| 光栅化 | 将几何图形转换为像素 | Rasterization |
| 片段处理 | 计算每个像素的颜色 | Fragment Shader |
| 测试与混合 | 深度测试、透明度混合 | Depth/Alpha Test |
6.2 纹理上传(Texture Upload)
图片资源需要先上传到 GPU 内存(Texture)。
学术定义:
- Bitmap 到 Texture: CPU 内存中的 Bitmap 数据需要拷贝到 GPU 内存。
- 性能瓶颈: 大图片的上传会导致 RenderThread 阻塞,引起掉帧。
7. 掉帧(Jank)在应用层的成因
7.1 UI 线程过载
| 原因 | 学术解释 |
|---|---|
| Measure/Layout 耗时过长 | View 树过于复杂,嵌套过深(如 RelativeLayout)。 |
| onDraw 执行繁重操作 | 在 onDraw() 中创建对象、进行复杂计算或 IO 操作。 |
| 过度绘制(Overdraw) | 多个 View 重叠绘制,GPU 需要重复绘制像素。 |
7.2 RenderThread 过载
| 原因 | 学术解释 |
|---|---|
| DisplayList 过大 | 复杂的 View 树导致大量绘制命令。 |
| 纹理上传过多 | 一帧内加载大量图片,导致 GPU 带宽饱和。 |
| Shader 编译 | 首次使用某些效果(如圆角、阴影)需要编译 Shader,导致卡顿。 |
8. 关键源码解析
8.1 Choreographer 的帧调度
java
// Choreographer.java
void doFrame(long frameTimeNanos, int frame) {
// 计算掉帧
long intendedFrameTimeNanos = frameTimeNanos;
long jitterNanos = frameTimeNanos - mLastFrameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
// 掉帧了!
skippedFrames = (int) (jitterNanos / mFrameIntervalNanos);
}
// 执行回调
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}
8.2 RenderNode 的属性动画
java
// RenderNodeAnimator.java
void animate(RenderNode node) {
// 直接修改 RenderNode 的属性,不触发 UI 线程重绘
node.setTranslationX(value);
node.setAlpha(alpha);
}
9. 本篇总结(Knowledge Closure)
| 关键点 | 纯学术定义 |
|---|---|
| UI 线程的角色 | 指挥官,负责 Measure/Layout 和 DisplayList 录制。 |
| RenderThread 的角色 | 执行者,负责将 DisplayList 转换为 GPU 指令。 |
| DisplayList 的价值 | 将绘制操作缓存为命令列表,支持异步执行和属性动画。 |
| 同步屏障 | 确保 UI 线程和 RenderThread 数据交换的线程安全。 |
| 硬件加速核心 | 利用 GPU 的并行处理能力,将 CPU 从繁重的像素填充中解放。 |
10. 第三板块结语
至此,第三板块:Android 图形渲染与窗口体系 已全部完结。
我们从 SurfaceFlinger 的合成引擎 出发,深入 VSYNC 的节拍控制 ,探索 BufferQueue 的缓冲机制 ,最终抵达 View 的绘制流水线 和 RenderThread 的异步渲染。
我们揭示了 Android 图形系统的设计哲学:用多层缓冲对抗时延,用硬件加速提升吞吐,用异步渲染解耦负载。
下一篇预告 :第四板块:Android 输入系统与触控事件 | 第十五篇:InputReader 与 InputDispatcher 的触控流水线