Android SurfaceFlinger 笔记

SurfaceFlinger(以下简称SF)与ActivityManagerService(AMS)是Android底层双雄。AMS管的是"谁活着、谁死去、谁在前台" ,是生命周期的CEOSF管的是"谁画了什么、怎么拼成一张图、何时送到屏幕" ,是视觉呈现的总导演

如果说AMS是Android多任务的大脑,SF就是Android图形系统的心脏 。它不生产像素(应用才是生产者),但它决定每一帧像素如何最终出现在你眼前。以下从六个维度彻底拆解SF。


一、核心定位:SF到底是什么?

SurfaceFlinger是一个系统级服务 ,运行在system_server进程,职责极度聚焦:接收所有应用和系统的图形缓冲区(Buffer),按Z轴顺序、透明度、裁剪区域等属性合成,通过硬件显示到屏幕

关键认知 :SF本身不做绘制 (Draw)。应用的onDraw()、OpenGL渲染、视频解码输出------这些都发生在应用进程。SF只做合成(Compose)。

一句话定性 :SF是Android的图形混合器显示调度中心

二、SF与AMS的"双核关系"

理解SF必须放在Android整体架构中。AMS和SF是SystemServer中耦合最深的两个服务:

维度 ActivityManagerService (AMS) SurfaceFlinger (SF)
管理对象 四大组件、进程、任务栈 Surface/Layer、图形缓冲区、显示设备
核心问题 谁应该在前台?哪个进程活着? 这块Buffer是谁的?怎么叠?何时送显?
触发信号 Intent、按键、系统事件 VSync中断(硬件时钟)
通信对象 应用主线程(ActivityThread) 应用渲染线程(RenderThread)或ViewRootImpl
纽带 WindowManagerService (WMS) ------ AMS通知WMS窗口焦点变化;WMS通过relayoutWindow()与SF交互,创建/更新Surface

核心结论AMS决定"演什么",WMS决定"怎么摆",SF决定"怎么拼、何时放"


三、SF的三大核心支柱

要彻底理解SF,必须啃下三块硬骨头:Buffer管理架构合成策略(GPU/HWC)VSync驱动模型

1. 架构基石:BufferQueue与生产-消费模型

这是Android图形系统最优雅的设计 ,没有之一。它实现了无拷贝、跨进程、异步的缓冲区流转 。

1.1 核心角色

  • 生产者(Producer) :应用进程(UI线程/RenderThread、Camera、视频解码器)。调用dequeueBuffer()获取空闲Buffer,填充数据,queueBuffer()归还。
  • 消费者(Consumer) :SurfaceFlinger。调用acquireBuffer()获取已就绪的Buffer,合成后releaseBuffer()释放。
  • 缓冲区队列(BufferQueue) :在SF进程 创建,Buffer本身是共享内存(GraphicBuffer),通过Binder传递文件描述符实现零拷贝跨进程传递 。

1.2 Buffer的四种生命状态(铁律)

  • FREE(空闲) :队列中无人使用。生产者可dequeue
  • DEQUEUED(出列) :生产者正在填充数据(如执行onDraw())。
  • QUEUED(入列):生产者完成绘制,等待SF消费。
  • ACQUIRED(获取):SF正在合成这一帧。

这是SF调度的最小单元。每一帧都是这四个状态的循环。

1.3 Surface与Layer的"皮肉关系"

  • Surface(皮) :应用侧看到的"画布",本质是IGraphicBufferProducer的代理。应用通过它lockCanvas()eglSwapBuffers()触发Buffer流转。
  • Layer(肉) :SF侧的"窗口代表"。每个Surface在SF内部对应一个Layer对象,记录Z-order、透明度、显示区域等元数据。
  • 创建时机 :WMS调用relayoutWindow() -> SF创建Layer -> 将BufferProducer Binder返回给应用 -> 应用构建Surface。

这就是窗口系统跨进程的完整握手

2. 合成引擎:GPU合成 vs. 硬件合成(HWC)

SF收到各Layer的Buffer后,必须把它们"拼"成一张完整的屏幕图像。怎么拼?两种模式,动态切换

2.1 GPU合成(OpenGL合成)

  • 方式:将所有Layer的Buffer作为纹理上传GPU,通过OpenGL绘制到一个目标FBO(帧缓冲对象)。
  • 优点:极其灵活,任意层数、任意形状、任意混合效果。
  • 缺点:耗电、带宽占用高。
  • 场景:界面复杂(多窗口)、动画效果、无法被HWC处理的图层。

2.2 硬件合成(HWC,Hardware Composer)

  • 方式 :将Layer信息(Buffer句柄、位置、Z-order)打包发给显示硬件,由显示控制器(Display Controller)硬件完成叠加,SF完全不碰像素数据 。
  • 优点:几乎零功耗、极低延迟。
  • 缺点:硬件叠图层数有限(通常4-8层),超出部分必须由GPU合成。
  • 场景:状态栏、导航栏、视频全屏层数少的场景。

2.3 HWC HAL契约

SF通过HAL层与硬件驱动交互。核心契约:

  • prepare():SF告诉HWC"我有这些Layer,你能硬件合成哪些?"
  • set():SF把需要硬件合成的Layer Buffer句柄传给HWC;需要GPU合成的部分,SF自己画好再传给HWC作为帧缓冲区(Framebuffer)
  • Fence机制:为了精确同步,Buffer的访问权通过同步栅栏(Fence FD)传递,避免CPU/GPU/显示控制器互相踩内存 。

3. 节拍器:VSync驱动模型

如果没有VSync,SF就是一盘散沙。 Android 4.1(Jelly Bean)引入VSync和Project Butter,这是SF调度模型的革命 。

3.1 硬件节拍

屏幕以固定频率刷新(通常是60Hz,即每16.6ms一帧)。硬件在每次刷新前会产生垂直同步中断(VSync) 。SF的工作就是卡在这个中断点上进行合成,杜绝"撕裂"(Tearing) 。

3.2 两条VSync线

SF内部维护两个独立的VSync分发通道

  1. VSync-app :分发给应用。Choreographer监听此信号,触发应用开始布局、绘制、dequeueBuffer
  2. VSync-sf :分发给SF自己。SF监听此信号,开始合成、acquireBuffer、调用HWC。

偏移量(Offset) :VSync-app通常比VSync-sf提前几毫秒 。这样应用画完Buffer入队时,SF刚好开始合成,流水线最大化

3.3 软件VSync预测

为了减少对硬件中断的依赖,SF维护一个软件VSync预测模型VSyncPredictor根据历史采样计算下一次中断时间,VSyncDispatchTimerQueue负责定时回调。这套机制保证了即使硬件暂时不稳定,UI依然流畅 。


四、一次完整的渲染合成闭环(六步法)

把以上组件串起来,看一个典型帧的生命周期:

Step 1:硬件心跳(VSync-app) 屏幕发出VSync中断 -> HWC HAL捕获 -> SF的EventThread唤醒 -> 通过BitTube跨进程通知应用的Choreographer

Step 2:应用生产(主线程+RenderThread) 应用收到VSync-app,执行doTraversal() -> 测量、布局、绘制 -> DisplayList转OpenGL命令 -> eglSwapBuffers()触发:

  • dequeueBuffer:从BufferQueue取空闲Buffer。
  • GPU渲染填充。
  • queueBuffer:Buffer入队,状态置为QUEUED,通过Binder回调SF的onFrameAvailable(),标记mQueuedFrames++

Step 3:SF唤醒(VSync-sf) 硬件发出VSync-sf(或软件定时触发)-> SF MessageQueue收到消息 -> 执行onMessageInvalidate() -> 正式启动合成流水线 。

Step 4:准备阶段(PreComposition & latchBuffer)

  • preComposition():遍历所有Layer,检查mQueuedFrames > 0,标记需要刷新的Layer。
  • latchBuffer():对标记的Layer调用acquireBuffer(),拿到应用刚刚画好的GraphicBuffer,所有权从应用转给SF

Step 5:合成决策(handleTransaction & rebuildLayerStacks & setUpHWComposer)

  • handleTransaction:处理窗口大小、位置、Z轴变化 。
  • rebuildLayerStacks:按Z轴排序当前屏幕可见Layer,计算每个Layer的脏区域、可见区域。
  • setUpHWComposer:调用HWC prepare()决策哪些Layer用硬件叠,哪些用GPU合成

Step 6:执行与送显(doComposition & postComposition)

  • GPU合成部分:SF通过OpenGL将选中的Layer绘制到目标FBO。
  • 调用HWC set():将GPU合成结果(Framebuffer)和硬件合成Layer的Buffer句柄一并交给驱动。
  • 释放Fence :通知应用侧"Buffer已被消费,可以继续画下一帧"。releaseBuffer() -> Buffer状态回FREE。
  • 屏幕刷新显示新帧。

至此,一帧结束。下一帧由下一个VSync触发。 16.6ms倒计时重新开始。


五、架构演进:从硬编码到高度抽象

SF的架构并非一成不变。理解演进史有助于读懂老代码。

时期 关键变化 技术驱动
Android 1.x-4.0 单缓冲、CPU合成为主 硬件弱,功能简单
Android 4.1 Project Butter:引入VSync、三缓冲、Choreographer 解决卡顿、掉帧
Android 5.0 RenderThread:将主线程的OpenGL操作移到单独线程 减少主线程负载,提高响应
Android 7.0 SurfaceView同步优化、HWC 2.0支持 配合VR、多屏显示
Android 8.x+ Treble :HAL层稳定化;AIDL化(类似AMS) 解耦Framework与厂商实现
Android 12+ 可变刷新率(VRR)、游戏模式插帧 高刷屏普及,追求能效比

当前趋势 :SF越来越像一个策略分发器,将具体合成工作下放给HWC,自己专注于状态跟踪和VSync调度。


六、开发层面的启示(实战价值)

理解了SF,你在应用开发中的很多"玄学"问题都会有根本性认知:

1. 为什么说"丢帧"本质是SF没等到Buffer?

如果应用主线程卡顿(GC、复杂布局),queueBuffer延迟,VSync-sf到来时SF无新帧可用,屏幕必须重复显示上一帧,这就是掉帧(Jank)。

2. 为什么SurfaceView播放视频不卡UI?

SurfaceView拥有独立的Surface ,其BufferQueue直接入队SF,不经过View层级,绕过主线程复杂绘制逻辑,同时常配HWC硬件叠层,效率极高 。

3. 为什么动画卡顿优先怀疑CPU/GPU,但有时是SF负载高?

SF虽然是合成者,但GPU合成非常耗资源。如果Z轴层数太多、HWC叠层用满,SF回退GPU合成,可能造成GPU过载、SF主线程超时16.6ms,导致下一帧延迟。

4. 为什么后台进程可能影响前台流畅度?

SF的Layer列表包含所有可见/不可见窗口 。后台应用若持有窗口(如无界面的WindowManager悬浮窗),其BufferQueue即使无新帧,Layer仍在列表中。极端情况下大量Layer导致SF rebuildLayerStacks耗时超标。

5. 终极调试工具

bash 复制代码
adb shell dumpsys SurfaceFlinger

这是SF的核磁共振报告 ,能查看每一层的Buffer申请记录、掉帧统计、HWC合成策略、GPU合成时间。分析卡顿的第一现场


结语

SurfaceFlinger和ActivityManagerService是Android底层架构的阴阳两面。AMS维系着应用世界的"存在与时间",SF维系着像素世界的"空间与流动"。

把AMS和SF放在一起理解,你会发现Android根本不是一个"单进程UI库",而是一个分布式、事件驱动、硬件感知的实时系统 。UI渲染不是函数的线性调用,而是多个特权进程在硬件节拍指挥下的环环相扣

每一帧的流畅,都是AMS、WMS、SF、App、GPU、Display硬件在16.6ms内的完美合奏。

相关推荐
似霰2 小时前
Android 日志系统5——logd 写日志过程分析二
android·log
hewence12 小时前
Kotlin CoroutineContext 详解
android·开发语言·kotlin
Albert Edison2 小时前
【Python】文件
android·服务器·python
大模型玩家七七3 小时前
效果评估:如何判断一个祝福 AI 是否“走心”
android·java·开发语言·网络·人工智能·batch
Aurora4193 小时前
Android事件分发逻辑--针对事件分发相关函数的讲解
android
似霰3 小时前
Android 日志系统4——logd 写日志过程分析一
android
youyoulg4 小时前
利用Android Studio编译Android上可直接执行的二进制
android·ide·android studio
闽农4 小时前
Android ANR 调用栈溯源
android·anr