一、使用方式:开发者如何触发显示更新
开发者只需要做简单的操作,系统会自动处理复杂的显示流程:
1. 修改 View 属性(最常见方式)
// 在 UI 线程中修改 View 属性
textView.setText("新内容"); // 修改文本
imageView.setImageBitmap(bitmap); // 更换图片
button.setBackgroundColor(Color.RED); // 改变背景色
// 系统自动触发重绘流程,无需手动调用绘制方法
2. 自定义 View 时重写绘制方法
public class MyCustomView extends View {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 在这里绘制自定义内容
canvas.drawCircle(100, 100, 50, paint);
}
// 当需要更新时
public void update() {
// 标记需要重绘
invalidate();
}
}
3. 创建动画效果
// 使用属性动画
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "translationX", 0f, 200f);
anim.setDuration(1000);
anim.start();
// 系统自动处理每一帧的更新和重绘
二、核心原理:显示流程的四个阶段
想象 View 显示就像制作一部动画电影:
1. 测量与布局阶段(Measure & Layout)
-
测量:计算每个 View 需要多大空间
// 伪代码 void measure(int widthSpec, int heightSpec) { // 根据父容器的约束计算自身尺寸 setMeasuredDimension(calculatedWidth, calculatedHeight); } -
布局:确定每个 View 的位置
java
复制
下载
// 伪代码 void layout(int left, int top, int right, int bottom) { // 确定自己在父容器中的位置 setFrame(left, top, right, bottom); }
2. 绘制阶段(Draw)
-
生成绘制指令列表(DisplayList)
-
不会立即执行绘制,只是记录操作:
// 伪代码 void draw(Canvas canvas) { drawBackground(canvas); // 记录背景绘制指令 onDraw(canvas); // 记录自定义内容指令 drawChildren(canvas); // 记录子View绘制指令 }
3. 渲染阶段(Render)
- 主线程:将绘制指令同步到 RenderThread
- RenderThread:将指令转换为 GPU 命令(OpenGL/Vulkan)
- GPU 执行实际渲染,输出到图形缓冲区
4. 显示阶段(Display)
- SurfaceFlinger 收集所有图层的缓冲区
- 在 VSYNC 信号到来时合成最终图像
- 发送到屏幕物理显示
三、核心机制:VSYNC 和 Choreographer
VSYNC(垂直同步)信号
- 显示器每 16.6ms(60Hz 屏幕)发出一次心跳信号
- 协调所有绘制操作,避免画面撕裂
- 三重缓冲机制防止卡顿:
Choreographer(编舞者)
-
协调 UI 更新与 VSYNC 信号
-
四种回调类型:
回调类型 执行内容 执行顺序 INPUT 触摸事件处理 1 ANIMATION 属性动画更新 2 TRAVERSAL 视图测量/布局/绘制 3 COMMIT 渲染结果提交 4
四、源码级执行流程(简化版)
1. 触发更新(例如调用 invalidate())
// View.java
public void invalidate() {
// 最终调用到 ViewRootImpl
parent.invalidateChild(this, dirtyRect);
}
// ViewRootImpl.java
void invalidateChild(View child, Rect dirty) {
scheduleTraversals(); // 关键:安排遍历
}
2. 安排绘制任务
// ViewRootImpl.java
void scheduleTraversals() {
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL,
mTraversalRunnable, // 包含performTraversals()
null
);
}
3. 等待并响应 VSYNC
// Choreographer 底层实现 (C++)
void Choreographer::dispatchVsync(...) {
// 回调到Java层
env->CallVoidMethod(mReceiver, gVsyncMethod, ...);
}
// Java层处理
void frameCallback(long frameTimeNanos) {
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}
4. 执行视图遍历三部曲
// ViewRootImpl.java
void performTraversals() {
// 1. 测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// 2. 布局
performLayout();
// 3. 绘制
performDraw();
}
5. 绘制实现细节
private void performDraw() {
// 使用硬件加速渲染器
mAttachInfo.mThreadedRenderer.draw(mView, ...);
}
// ThreadedRenderer.java
void draw(View view, ...) {
// 1. 更新显示列表
updateViewTreeDisplayList(view);
// 2. 同步到渲染线程
nSyncAndDrawFrame(mNativeProxy, ...);
}
6. 渲染线程工作流程
// RenderProxy.cpp (native层)
void RenderProxy::syncAndDrawFrame() {
// 创建OpenGL/Vulkan命令
CanvasContext* context = getContext();
context->draw();
}
// CanvasContext.cpp
void draw() {
// 1. 准备帧
makeCurrent();
// 2. 执行所有GL绘制命令
renderFrame();
// 3. 交换缓冲区
swapBuffers();
}
7. 显示到屏幕
// SurfaceFlinger.cpp
void handleMessageRefresh() {
// 1. 收集所有图层
Vector<Layer*> layers(mVisibleLayers);
// 2. 合成图层
for (auto layer : layers) {
layer->latchBuffer(); // 获取缓冲区
layer->setPerFrameData(); // 设置每帧数据
}
// 3. 最终合成
doComposition();
// 4. 发送到显示器
postFramebuffer();
}
七、总结:完整的显示流程全景图
这个流程就像精心设计的工厂流水线:
- 设计部门(UI线程):设计产品图纸(测量布局)
- 工程部门(UI线程):编写生产指令(生成显示列表)
- 制造部门(RenderThread):操作机器生产(GPU渲染)
- 质检部门(SurfaceFlinger):组装最终产品(图层合成)
- 物流部门(显示系统):按时发货(VSYNC同步显示)
每一帧都经过这样的流程,60Hz 屏幕意味着每秒要完成 60 次这样的完整流程(约 16.6ms/帧)。任何环节超时都会导致掉帧(jank),这就是为什么性能优化如此重要