- Surface 到底是什么?它和 View/Window 的关系是什么?
- App 绘制的像素数据是如何存储在 Surface 中的?
- Surface 是如何跨进程把数据传递给 SurfaceFlinger 的?
Surface 的定义
Surface 不是 “显示组件”,而是 「一块可绘制的内存缓冲区(Buffer)的抽象封装」。
通俗理解:Surface 是 App 写给 “画布” 的 “画板”,App 只负责往画板上画画(写入像素数据),但不负责把画板挂到墙上(显示到屏幕);
技术本质:Surface 内部持有 BufferQueue 的「生产者(Producer)」端,通过它向缓冲区写入数据。
Surface 与 Window/View 的关系
1 个 Window 对应 1 个 Surface:每个 Activity/Dialog/ 状态栏都对应一个 Window,每个 Window 会创建一个专属的 Surface(通过 WindowManager 申请);
View 依赖 Surface 绘制:所有 View 的 onDraw() 最终都是通过 Canvas 把数据写入所属 Window 的 Surface 缓冲区;
Surface 与 ViewRootImpl 绑定:ViewRootImpl 是 Surface 和 View 树的桥梁,负责把 View 绘制的内容同步到 Surface 中。
Activity 启动 → WindowManager 创建 Window
↓
WindowManager 向 SurfaceFlinger 申请 Surface
↓
SurfaceFlinger 创建 BufferQueue,并返回「生产者」给 App 层
↓
App 层封装生产者为 Surface 对象,绑定到 ViewRootImpl
↓
ViewRootImpl 把 Surface 传递给 Canvas,供 View 绘制
- 关键结论:Surface 的创建是跨进程的,由 SurfaceFlinger 主导,App 层只持有 “写入数据的权限”。
BufferQueue - 缓冲区的「生产者 - 消费者」模型
BufferQueue 是连接 App 和 SurfaceFlinger 的核心机制,本质是 「环形缓冲区队列」,遵循生产者 - 消费者设计模式:
生产者(Producer):App 层(Surface 持有),负责往缓冲区写入绘制好的像素数据;
消费者(Consumer):SurfaceFlinger 层,负责从缓冲区读取数据,进行图层合成。
BufferQueue 的核心作用
解耦 App 绘制和 SurfaceFlinger 合成:App 绘制的速度和 SurfaceFlinger 合成的速度可以不一致,通过缓冲区队列缓冲;
复用缓冲区:避免频繁创建 / 销毁内存缓冲区,减少内存抖动和性能消耗;
同步数据:保证 SurfaceFlinger 读取的缓冲区是 “完整绘制完成” 的,避免读取到半拉子数据。
BufferQueue 的工作流程(60Hz 屏幕为例)
1. 初始状态:BufferQueue 有 N 个空缓冲区(通常 2-3 个);
2. App 向 BufferQueue 请求一个空缓冲区(dequeueBuffer);
3. App 通过 Canvas 把绘制内容写入该缓冲区;
4. App 把写好的缓冲区送回队列(queueBuffer),标记为“就绪”;
5. SurfaceFlinger 从队列取出就绪的缓冲区(acquireBuffer);
6. SurfaceFlinger 合成该缓冲区的数据到最终帧;
7. SurfaceFlinger 把用完的缓冲区放回队列(releaseBuffer),标记为“空”;
8. 重复步骤 2-7,每 16.6ms 执行一次(60Hz 屏幕)。
双缓冲 / 三缓冲机制
双缓冲:BufferQueue 有 2 个缓冲区,一个供 App 写入,一个供 SurfaceFlinger 读取,避免 “写一半被读” 的问题;
三缓冲:当 App 绘制耗时超过 16.6ms 时,第三个缓冲区可以承接下一次绘制,减少掉帧(比如 120Hz 屏幕常用三缓冲)。
Surface + BufferQueue 核心
// Surface 中的生产者逻辑(App 层)
public class Surface {
private final SurfaceControl mSurfaceControl;
private final BufferQueueProducer mProducer; // 生产者
// 申请空缓冲区
public long dequeueBuffer(BufferItem item, long timeout) {
return mProducer.dequeueBuffer(item, timeout);
}
// 提交写好的缓冲区
public int queueBuffer(long bufferId, int flags) {
return mProducer.queueBuffer(bufferId, flags);
}
}
// SurfaceFlinger 中的消费者逻辑(Native 层简化)
class SurfaceFlinger {
private BufferQueueConsumer mConsumer; // 消费者
// 获取就绪的缓冲区
status_t acquireBuffer(BufferItem* item, nsecs_t timeout) {
return mConsumer->acquireBuffer(item, timeout);
}
// 释放用完的缓冲区
status_t releaseBuffer(int slot, uint64_t frameNumber) {
return mConsumer->releaseBuffer(slot, frameNumber);
}
}
Surface → SurfaceFlinger 的数据传递
跨进程通信的核心:Binder
Surface 持有的 BufferQueueProducer 是 Binder 接口,SurfaceFlinger 持有的 BufferQueueConsumer 是 Binder 服务端:
App 层调用 queueBuffer() 时,通过 Binder 把缓冲区的「句柄(Handle)」传递给 SurfaceFlinger(而非直接传递像素数据,避免内存拷贝);
SurfaceFlinger 通过句柄直接访问缓冲区内存(基于共享内存 Ashmem),无需拷贝,效率极高。
数据传递完整流程
App 层:View.onDraw() → Canvas 写入像素数据
↓
Canvas 把数据写入 Surface 持有的 BufferQueue 缓冲区
↓
Surface.queueBuffer() → 通过 Binder 把缓冲区句柄发给 SurfaceFlinger
↓
SurfaceFlinger.acquireBuffer() → 获取缓冲区句柄,访问共享内存
↓
SurfaceFlinger 合成该缓冲区(与其他 Surface 的缓冲区)→ 输出到屏幕
- 关键结论:像素数据存储在共享内存中,只传递句柄,不拷贝数据,这是 Android 渲染性能的核心优化点。
SurfaceView vs TextureView
普通 View 依赖 Window 的 Surface 绘制,而高频绘制场景(视频、游戏、直播)需要更高效的 Surface 载体:

问题
为什么 SurfaceFlinger 不直接读取 App 内存中的像素数据,而是通过 BufferQueue 共享内存?
为什么 SurfaceView 适合高频绘制场景,而普通 View 不适合?