Surface渲染原理
# 09.Surface渲染原理
# 目录介绍
- 一、概述
- 二、问题背景
- 2.1 View绘制的局限性
- 2.2 新的诉求
- 三、Android图形架构
- 3.1 Surface图形架构
- 3.2 BufferQueue机制
- 3.3 SurfaceFlinger合成原理
- 3.4 VSYNC与Choreographer
- 四、SurfaceView深入分析
- 4.1 SurfaceView的设计目的
- 4.2 SurfaceView的MVC架构
- 4.3 SurfaceView的"挖洞"机制
- 4.4 子图层类型
- 4.5 SurfaceView为何不卡顿
- 4.6 SurfaceView的生命周期管理
- 4.7 SurfaceView的限制
- 五、TextureView深入分析
- 5.1 TextureView的设计目的
- 5.2 TextureView的工作原理
- 5.3 TextureView的要求
- 5.4 TextureView的不足
- 六、SurfaceView与TextureView对比
- 6.1 优缺点对比
- 6.2 核心原理差异
- 七、选择策略
- 7.1 优先选择SurfaceView的场景
- 7.2 选择TextureView的场景
- 7.3 总结建议
- 八、MediaPlayer视频渲染流程
- 8.1 MediaPlayer与Surface的绑定
- 8.2 硬件解码与软件解码
- 九、GLSurfaceView与OpenGL渲染
- 9.1 GLSurfaceView的设计
- 9.2 渲染模式
- 十、总结与面试高频问题
- 10.1 知识图谱
- 10.2 面试高频问题
# 一、概述
在Android中,普通View的绘制必须在主线程完成,当面对视频播放、相机预览、游戏渲染等高频绘制场景时,容易造成UI卡顿。为此Android提供了SurfaceView和TextureView两种特殊视图,它们拥有独立的渲染能力。本文深入分析两者的设计原理、使用场景和选择策略。
# 二、问题背景
# 2.1 View绘制的局限性
Android的UI操作必须在主线程中执行,原因是:
- UI操作涉及View树内部大量状态维护,多线程读写会引发内部状态混乱
- 通过加锁保证线程同步会带来巨大性能开销,且使UI刷新变得复杂
View通过VSYNC信号重绘,系统要求在16ms内完成绘制。如果绘制逻辑复杂且更新频繁,就会造成卡顿。
View绘制的具体限制:
1. 主线程瓶颈
普通View的绘制流程:
VSync → Choreographer → measure → layout → draw → DisplayList → RenderThread
其中measure/layout/draw都在主线程执行
→ 如果onDraw中做复杂计算(如视频帧渲染)→ 主线程被占用
→ 同时间的触摸事件无法处理 → 用户感知卡顿
2. 帧率限制
普通View的刷新受VSync周期限制(60fps = 16.6ms)
→ 且要和其他View共享主线程时间片
→ 游戏/视频等场景需要独占渲染线程
3. Surface共享问题
同一个Window中的所有View共享一个Surface
→ 一个View的重绘可能导致整个View树重绘
→ 无法为单个View单独设置帧率或缓冲策略
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2.2 新的诉求
开发者需要一种特殊视图:
- 拥有独立的Surface,脱离Activity宿主的限制
- 可以在独立线程中进行绘制
- 不占用主线程资源,保证输入事件及时响应
独立Surface的核心优势:
1. 独立线程渲染
普通View:主线程 → measure → layout → draw → 共享Surface
SurfaceView:独立线程 → lockCanvas → draw → unlockAndPost → 独立Surface
→ 渲染不阻塞主线程,主线程可以继续处理输入事件
2. 独立帧率
普通View的帧率由系统VSync统一调度(60/90/120fps)
独立Surface可以按需控制帧率:
→ 视频播放:24fps/30fps/60fps
→ 游戏:尽可能高的帧率
→ 相机预览:30fps
3. 独立的BufferQueue
每个Surface有自己的BufferQueue(默认3个Buffer)
→ 不与Activity的Surface竞争Buffer
→ SurfaceFlinger独立合成该Layer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 三、Android图形架构
# 3.1 Surface图形架构
Android图形架构:
Activity → Window → Surface(绘图表面)
↓
图形缓冲区队列(内存)
↓
WindowManagerService(通信)
↓
SurfaceFlinger(合成送显)
↓
显示屏
2
3
4
5
6
7
8
9
10
11
每一个Activity关联至少一个Window,每一个Window对应一个Surface。Surface是一块内存区域,所有View的UI数据最终填充到同一个Surface中,由SurfaceFlinger合成并显示到屏幕。
# 3.2 BufferQueue机制
BufferQueue是Android图形架构的核心组件,它连接图形数据的生产者和消费者:
BufferQueue生产者-消费者模型:
生产者(Producer) 消费者(Consumer)
┌──────────────┐ ┌──────────────┐
│ App / Surface │ │SurfaceFlinger │
│ │ │ │
│ dequeueBuffer│──────────────►│ │
│ (获取空闲Buffer)│ │ │
│ │ │ │
│ 在Buffer上绘制│ │ │
│ │ │ │
│ queueBuffer │──────────────►│acquireBuffer │
│ (提交Buffer) │ │(获取已填充Buffer)│
│ │ │ │
│ │ │ 合成送显 │
│ │ │ │
│ │◄──────────────│releaseBuffer │
│ │ │(归还Buffer) │
└──────────────┘ └──────────────┘
Buffer状态流转:
FREE → DEQUEUED → QUEUED → ACQUIRED → FREE
↑ │
└───────────────────────────────────────┘
BufferQueue默认包含3个GraphicBuffer(三缓冲):
┌─────────────────────────────────────┐
│ Buffer[0]: ACQUIRED (正在被SurfaceFlinger显示)│
│ Buffer[1]: QUEUED (已绘制完等待消费) │
│ Buffer[2]: DEQUEUED (正在被App绘制) │
└─────────────────────────────────────┘
三缓冲相比双缓冲的优势:
双缓冲:如果SurfaceFlinger还没消费完,App只能等待
三缓冲:App可以继续在第三个Buffer上绘制,减少等待
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 3.3 SurfaceFlinger合成原理
SurfaceFlinger的工作流程:
VSYNC信号到来
│
├── 1. 收集所有Layer(每个Surface对应一个Layer)
│ ├── Activity的Surface → Layer
│ ├── SurfaceView的Surface → Layer
│ ├── 状态栏Surface → Layer
│ └── 导航栏Surface → Layer
│
├── 2. 计算每个Layer的可见区域
│ └── 考虑层级、透明度、裁剪区域
│
├── 3. 合成策略选择
│ ├── Client合成(GPU合成)
│ │ └── 使用OpenGL ES将所有Layer渲染到一个Framebuffer
│ │ └── 耗电,但灵活性高
│ └── Device合成(Hardware Composer / HWC)
│ └── 直接由显示硬件合成多个Layer
│ └── 省电,但有Layer数量和格式限制
│ └── 通常优先使用HWC
│
└── 4. 送显
└── 将最终的Framebuffer发送到显示驱动
└── 显示控制器读取并输出到屏幕
HWC(Hardware Composer)决策:
对于每个Layer,HWC判断:
├── 可以硬件合成 → 标记为HWC_OVERLAY
└── 不能硬件合成 → 标记为HWC_FRAMEBUFFER(回退到GPU)
不能硬件合成的情况:
├── Layer数量超过HWC支持的最大值(通常4-8个)
├── Layer需要复杂的颜色变换
├── Layer需要特殊的混合模式
└── Layer尺寸超过HWC处理能力
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 3.4 VSYNC与Choreographer
VSYNC同步机制:
硬件VSYNC(来自显示面板):
└── 通知显示控制器可以切换帧缓冲
└── 典型频率:60Hz(16.67ms)/ 90Hz / 120Hz
软件VSYNC(DispSync):
└── SurfaceFlinger根据硬件VSYNC生成的软件信号
└── 分为两个偏移的信号:
├── VSYNC-app:通知App开始绘制下一帧
│ └── 偏移量通常为1-2ms
└── VSYNC-sf:通知SurfaceFlinger开始合成
└── 偏移量通常为硬件VSYNC后几ms
时间线(理想情况):
Frame N:
├── VSYNC-app → App绘制Frame N+2
├── VSYNC-sf → SurfaceFlinger合成Frame N+1
└── 显示器显示Frame N
Choreographer(编舞者):
└── 接收VSYNC-app信号
└── 按优先级执行回调:
├── CALLBACK_INPUT (输入事件)
├── CALLBACK_ANIMATION (动画)
├── CALLBACK_INSETS_ANIMATION
├── CALLBACK_TRAVERSAL (View遍历:measure/layout/draw)
└── CALLBACK_COMMIT (提交)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 四、SurfaceView深入分析
# 4.1 SurfaceView的设计目的
SurfaceView拥有独立的Surface,与普通View共享宿主Activity的Surface不同:
普通View vs SurfaceView:
普通View:
Activity Surface(包含所有View的UI数据)
→ SurfaceFlinger → 显示
SurfaceView:
Activity Surface(其他View的UI数据)
↘
SurfaceFlinger → 合成 → 显示
↗
SurfaceView Surface(独立的绘制数据)
2
3
4
5
6
7
8
9
10
11
12
# 4.2 SurfaceView的MVC架构
SurfaceView采用MVC设计模式:
- Model(Surface):视图数据,实现了Parcelable接口,内部包含Canvas和缓冲区
- View(SurfaceView):显示视图,与用户交互的界面
- Controller(SurfaceHolder):控制器接口,用于管理绘制操作
Surface核心方法:
lockCanvas():获取绘画的Canvas对象,此时Canvas被锁定unlockCanvasAndPost():释放Canvas并将新绘制的内容提交显示
# 4.3 SurfaceView的"挖洞"机制
SurfaceView在z轴上默认低于DecorView(在页面最下方),为了让底层视图可见,SurfaceView在宿主Activity的窗口上设置了一块透明区域——即"挖洞"。
// SurfaceView源码关键流程
@Override
protected void onAttachedToWindow() {
// 1. 向父视图请求创建透明区域(挖洞)
mParent.requestTransparentRegion(SurfaceView.this);
// 2. 注册绘制前监听
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnPreDrawListener(mDrawListener);
}
// 3. 绘制前创建独立的Surface
protected void updateSurface() {
mSurfaceSession = new SurfaceSession();
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
流程解析:
- SurfaceView向父视图逐层请求透明区域,根视图通过Binder通知WMS将对应区域设为透明
- 建立与独立绘图表面的连接(SurfaceSession),创建控制器(SurfaceControl)
- 后续可与独立Surface直接通信进行绘制
# 4.4 子图层类型
SurfaceView通过mSubLayer控制其在z轴上的位置:
// 默认值-2,表示在Activity下方
int mSubLayer = APPLICATION_MEDIA_SUBLAYER; // -2
// 子图层层级
APPLICATION_MEDIA_SUBLAYER = -2; // 视频层(默认)
APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1; // 字幕层
APPLICATION_PANEL_SUBLAYER = 1; // 面板层(Activity上方)
2
3
4
5
6
7
setZOrderOnTop(true):将SurfaceView放到Activity上方setZOrderMediaOverlay(true):用于字幕场景,在视频层之上但Activity之下
# 4.5 SurfaceView为何不卡顿
两方面保障:
- 双缓冲技术:使用frontCanvas和backCanvas,绘制在backCanvas上完成后与frontCanvas交换,避免屏幕闪烁
- 独立线程绘制:在子线程更新UI,不阻塞主线程,保证输入事件响应速度
双缓冲工作原理:
双缓冲机制:
lockCanvas() → 获取backCanvas → 在backCanvas上绘制
→ unlockCanvasAndPost() → backCanvas变为frontCanvas显示
→ 原frontCanvas变为backCanvas → 下次绘制使用
2
3
4
5
实际上SurfaceView使用的是BufferQueue的多缓冲机制(默认三缓冲),而非简单的前后缓冲交换。当调用lockCanvas()时,实际是从BufferQueue中dequeue一个空闲的GraphicBuffer;unlockCanvasAndPost()则将该Buffer queue回去等待SurfaceFlinger消费。
SurfaceView的渲染管线:
应用层:
lockCanvas()
→ Surface.lock()
→ BufferQueue.dequeueBuffer()
→ 获取一个空闲的GraphicBuffer
→ 将GraphicBuffer映射为Canvas
在Canvas上绘制...
unlockCanvasAndPost()
→ Surface.unlockCanvasAndPost()
→ BufferQueue.queueBuffer()
→ 将已绘制的Buffer提交到队列
→ 触发SurfaceFlinger的onFrameAvailable回调
SurfaceFlinger层:
onFrameAvailable()
→ 标记该Layer有新帧
→ 下一个VSYNC-sf时:
→ acquireBuffer() 获取新帧
→ 与其他Layer合成
→ 送显
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 4.6 SurfaceView的生命周期管理
SurfaceView的Surface与Activity的Window有不同的生命周期:
SurfaceView生命周期回调:
SurfaceHolder.Callback:
surfaceCreated(holder) → Surface首次创建(可以开始绘制)
surfaceChanged(holder, format, width, height) → Surface尺寸/格式改变
surfaceDestroyed(holder) → Surface将被销毁(必须停止绘制)
与Activity生命周期的关系:
Activity.onCreate() → SurfaceView被添加到View树
Activity.onResume() → surfaceCreated() (Surface创建)
Activity.onPause() → surfaceDestroyed() (Surface销毁)
Activity.onDestroy() → SurfaceView从View树移除
关键点:
Surface在Activity不可见时会被销毁
恢复时会重新创建新的Surface
因此在surfaceCreated中初始化资源
在surfaceDestroyed中释放资源
MediaPlayer / Camera等数据源
必须在surfaceCreated后才能绑定到Surface
必须在surfaceDestroyed前解绑
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 4.7 SurfaceView的限制
- 不支持View动画:SurfaceView不在View hierarchy的渲染流程中,平移、缩放、旋转等变换无效
- 不能嵌套使用:在ListView、ScrollView等容器中使用可能出现问题
- 无法截图:SurfaceView的draw方法只绘制透明窗口,实际内容在独立Surface中
为什么SurfaceView取不到图片?
// SurfaceView的draw方法
public void draw(Canvas canvas) {
// 只是在View层打一个透明的洞
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
super.draw(canvas); // onDraw是空实现
}
2
3
4
5
6
SurfaceView的展示内容不是通过View的draw流程绘制的,而是在独立的Surface中,因此getDrawingCache()获取不到图像。
# 五、TextureView深入分析
# 5.1 TextureView的设计目的
TextureView是为了解决SurfaceView不支持动画和截图的问题而设计的:
- 跟随ViewRootImpl的三大流程绘制
- 通过TextureLayer和硬件加速进行渲染
- 支持移动、旋转、缩放等View属性动画
# 5.2 TextureView的工作原理
TextureView渲染流程:
数据源(MediaPlayer/Camera)
↓
SurfaceTexture(数据通道)
↓ 转为GL外部纹理
TextureLayer(硬件加速层)
↓
View hierarchy中正常渲染
↓
屏幕显示
2
3
4
5
6
7
8
9
10
11
关键组件:
- SurfaceTexture:作为数据通道,将数据源的图像帧数据转为OpenGL纹理
- TextureLayer:作为View hierarchy中的硬件加速层来显示
# 5.3 TextureView的要求
- 必须开启硬件加速:TextureView依赖硬件渲染的DisplayListCanvas进行绘制
- 使用SurfaceTexture关联数据源(如MediaPlayer),在onSurfaceTextureAvailable回调中设置
// TextureView的基本使用
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int w, int h) {
// 方式1:关联MediaPlayer
Surface surface = new Surface(surfaceTexture);
mediaPlayer.setSurface(surface);
// 方式2:关联Camera
camera.setPreviewTexture(surfaceTexture);
camera.startPreview();
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// 返回true表示由系统释放SurfaceTexture
// 返回false表示由开发者手动释放(异步释放场景)
return true;
}
// 其他回调:onSurfaceTextureSizeChanged, onSurfaceTextureUpdated
});
// 注意:如果Activity的硬件加速被关闭
// TextureView会显示黑屏且不报错
// 检查方式:textureView.isHardwareAccelerated()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 5.4 TextureView的不足
- 增加1~3帧的延迟(由于失效和缓冲特性)
- 总是使用GL合成,比SurfaceView占用更多内存带宽和能量
- 内部缓冲队列导致比SurfaceView使用更多内存
- 5.0以前在主线程渲染,5.0以后有单独的渲染线程
TextureView性能问题的底层原因:
延迟原因(1-3帧):
数据源产生帧 → SurfaceTexture缓冲 → updateTexImage()
→ 等待下一次VSync → View.draw()中绘制纹理
→ RenderThread执行 → SurfaceFlinger合成
→ 比SurfaceView多了SurfaceTexture缓冲和View绘制两个环节
内存占用更高的原因:
SurfaceView:3个GraphicBuffer(三缓冲)
TextureView:3个GraphicBuffer + GL纹理 + 可能的CPU拷贝
→ 1080p视频:SurfaceView约24MB,TextureView约32-40MB
功耗更高的原因:
SurfaceView:SurfaceFlinger直接合成(可能HWC硬件合成)
TextureView:必须通过GPU合成(纹理→View层级→Surface)
→ GPU合成比HWC硬件合成耗电约2-3倍
DRM内容限制:
受保护的视频内容(如Netflix DRM)
→ 必须使用SurfaceView的Secure Surface
→ TextureView无法显示受DRM保护的内容
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 六、SurfaceView与TextureView对比
# 6.1 优缺点对比
| 特性 | SurfaceView | TextureView |
|---|---|---|
| 独立Surface | ✓ | ✗(共享宿主Surface) |
| 独立线程绘制 | ✓ | 5.0+有独立渲染线程 |
| 双缓冲 | ✓ | ✗ |
| 支持动画 | ✗ | ✓ |
| 支持截图 | ✗ | ✓ |
| 硬件加速要求 | 无 | 必须开启 |
| 内存占用 | 较低 | 较高 |
| 渲染延迟 | 低 | 额外1~3帧 |
| 嵌套使用 | 受限 | 支持 |
# 6.2 核心原理差异
SurfaceView:
- 创建独立于应用窗口之后的新窗口
- 刷新时不需要重绘应用程序窗口
- 因为不在应用窗口上,所以不能使用View变换
- 通过SurfaceHolder的Callback回调管理Surface的创建和销毁
TextureView:
- 内部创建SurfaceTexture后,用其关联MediaPlayer作为数据源
- SurfaceTexture将图像帧数据转为GL外部纹理
- 交给TextureView作为View hierarchy中的硬件加速层显示
# 七、选择策略
# 7.1 优先选择SurfaceView的场景
从性能和安全性角度出发,以下场景优先选择SurfaceView:
- Android 7.0+系统,SurfaceView性能优势明显
- 不需要动画效果的视频播放
- 游戏渲染等高帧率场景
- 对内存和功耗敏感的场景
SurfaceView优先的详细理由:
Android 7.0+的SurfaceView改进:
→ 支持同步移动(SurfaceView随父View同步滚动)
→ 大大减少了画面不同步问题
→ 成为Google官方推荐的视频/游戏渲染方案
性能优势量化:
→ 少一次GPU合成操作 → 省约2ms/帧
→ 不占用主线程 → 主线程掉帧不影响视频流畅度
→ 独立BufferQueue → Buffer管理更高效
→ 内存占用比TextureView低约30%
ExoPlayer/MediaPlayer推荐:
→ Google的ExoPlayer默认使用SurfaceView
→ DRM安全内容必须使用SurfaceView(TextureView无法显示受保护内容)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 7.2 选择TextureView的场景
- 7.0以下系统需要动画效果时
- 需要截图功能
- 需要在ScrollView等滑动容器中使用
- 需要进行View属性动画(如视频小窗口飘动效果)
TextureView必选的场景:
视频小窗口(悬浮窗/画中画):
→ 需要拖拽移动(translation动画)
→ 需要缩放(scale动画)
→ 需要圆角裁剪(clipOutline)
→ SurfaceView无法实现这些效果
视频截图/录屏:
→ TextureView.getBitmap()直接获取帧内容
→ SurfaceView需要使用PixelCopy(Android 7.1+)或MediaProjection
视频列表(如抖音/微博):
→ RecyclerView中的视频需要随列表滚动
→ Android 7.0以下SurfaceView滚动不同步
→ 7.0+两者都可以,但TextureView在列表复用时更简单
视频转场动画:
→ 需要共享元素动画(Shared Element Transition)
→ TextureView作为普通View可以参与转场
→ SurfaceView无法参与View转场动画
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 7.3 总结建议
选择决策树:
需要View动画/截图?
├── 是 → Android 7.0+?
│ ├── 是 → SurfaceView(7.0+支持同步更新)
│ └── 否 → TextureView
└── 否 → SurfaceView(性能更优)
2
3
4
5
6
7
# 八、MediaPlayer视频渲染流程
# 8.1 MediaPlayer与Surface的绑定
MediaPlayer视频播放的完整渲染流程:
1. 创建并设置Surface
SurfaceView.surfaceCreated(holder)
→ MediaPlayer.setSurface(holder.getSurface())
→ 在Native层将Surface传给MediaCodec
2. 解码与渲染管线
MediaPlayer
→ MediaExtractor(解封装:MP4→视频轨+音频轨)
→ MediaCodec(硬件解码器)
→ 输入:压缩的视频帧(H.264/H.265 NAL单元)
→ 输出:解码后的GraphicBuffer(YUV格式)
→ 直接渲染到Surface的BufferQueue中
→ SurfaceFlinger合成送显
3. 零拷贝渲染
MediaCodec解码器配置Surface后:
├── 解码器输出Buffer直接是Surface的GraphicBuffer
├── 解码完成后自动queueBuffer到BufferQueue
├── 无需应用层拷贝数据
└── 从解码到显示全程零拷贝
解码器输出 BufferQueue SurfaceFlinger
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 解码帧 │──queue────►│ 已解码帧 │──acquire──►│ 合成显示 │
│ (GPU内存) │ │ (等待显示)│ │ │
└──────────┘ └──────────┘ └──────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 8.2 硬件解码与软件解码
视频解码方式对比:
硬件解码(推荐):
├── 使用设备的专用视频解码芯片(DSP/VPU)
├── 功耗低(比CPU低10倍以上)
├── 速度快(可处理4K60fps)
├── 输出直接在GPU内存中,零拷贝送显
├── 缺点:兼容性受硬件限制
└── Android对应:MediaCodec配置CONFIGURE_FLAG_ENCODE
软件解码:
├── 使用CPU执行解码算法
├── 功耗高(CPU满载)
├── 速度慢(4K视频可能卡顿)
├── 输出在CPU内存中,需要拷贝到GPU
├── 优点:兼容性好,所有格式都能解
└── Android对应:FFmpeg等第三方库
MediaCodec查找解码器优先级:
1. 硬件解码器(名称前缀OMX.xxx)
2. 软件解码器(名称前缀c2.android.xxx)
示例:
H.264解码器查找:
├── OMX.qcom.video.decoder.avc (高通硬件解码)
├── OMX.Exynos.avc.dec (三星硬件解码)
└── c2.android.avc.decoder (Android软件解码,兜底)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 九、GLSurfaceView与OpenGL渲染
# 9.1 GLSurfaceView的设计
GLSurfaceView是SurfaceView的子类,专为OpenGL ES渲染设计:
GLSurfaceView的架构:
GLSurfaceView
├── 继承SurfaceView(独立Surface)
├── 内置GLThread(OpenGL渲染线程)
├── 管理EGL上下文(OpenGL与Surface的桥梁)
└── 提供Renderer接口
GLThread工作流程:
┌─────────────────────────────┐
│ GLThread │
│ │
│ 1. eglCreateContext() │ ← 创建OpenGL上下文
│ 2. eglMakeCurrent() │ ← 绑定到当前线程
│ │
│ loop { │
│ 3. Renderer.onDrawFrame() │ ← 用户绘制逻辑
│ 4. eglSwapBuffers() │ ← 交换前后缓冲
│ → queueBuffer() │ 将帧提交到BufferQueue
│ 5. 等待VSYNC/requestRender│
│ } │
│ │
│ 6. eglDestroyContext() │ ← 销毁OpenGL上下文
└─────────────────────────────┘
EGL的角色:
EGL是OpenGL ES与窗口系统之间的桥梁
EGLDisplay → 物理显示设备的抽象
EGLConfig → 像素格式配置(颜色深度、深度缓冲等)
EGLSurface → 渲染目标(与Surface绑定)
EGLContext → OpenGL状态机(着色器、纹理等)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 9.2 渲染模式
GLSurfaceView的两种渲染模式:
1. RENDERMODE_CONTINUOUSLY(连续渲染)
└── GLThread不断调用onDrawFrame()
└── 适合游戏等持续更新的场景
└── 每帧都重新渲染
2. RENDERMODE_WHEN_DIRTY(按需渲染)
└── 只在requestRender()时调用onDrawFrame()
└── 适合3D模型展示等偶尔更新的场景
└── 节省电量和CPU/GPU资源
设置方式:
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
// 需要更新时主动请求渲染
glSurfaceView.requestRender();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 十、总结与面试高频问题
# 10.1 知识图谱
视频渲染View知识图谱:
SurfaceView
├── 独立Surface → 独立线程绘制
├── 挖洞机制 → 透明区域 + z轴分层
├── BufferQueue → 生产者-消费者模型(三缓冲)
├── 子图层类型 → 控制z轴位置
├── 生命周期 → surfaceCreated/Changed/Destroyed
└── 限制 → 不支持动画、截图
TextureView
├── 共享Surface → 硬件加速渲染
├── SurfaceTexture → GL外部纹理
├── View hierarchy → 支持动画和变换
└── 限制 → 额外延迟、更多内存
图形架构
├── Surface → 绘图表面(内存缓冲区)
├── BufferQueue → 生产者-消费者模型
├── SurfaceFlinger → 层合成与送显
│ ├── HWC硬件合成(省电)
│ └── GPU合成(灵活)
├── VSYNC → 垂直同步信号
│ ├── VSYNC-app → 通知App绘制
│ └── VSYNC-sf → 通知SurfaceFlinger合成
└── Choreographer → 帧调度器
视频播放管线
├── MediaExtractor → 解封装
├── MediaCodec → 硬件/软件解码
├── Surface → 零拷贝渲染
└── SurfaceFlinger → 合成送显
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 10.2 面试高频问题
问题:SurfaceView和TextureView的区别?
- SurfaceView有独立Surface,在单独的Window中渲染,不参与View hierarchy
- TextureView共享宿主Surface,通过SurfaceTexture转为GL纹理在View hierarchy中渲染
- SurfaceView不支持View动画和截图,TextureView支持
- SurfaceView性能更好(少一次GPU合成),TextureView有1-3帧额外延迟
问题:SurfaceView的"挖洞"机制是什么?
- SurfaceView的Surface在z轴上位于Activity Window之下
- 为了让底层内容可见,SurfaceView在宿主Window上设置透明区域
- 通过requestTransparentRegion逐层向上传递,最终通知WMS
- WMS在合成时将该区域设为透明,露出底层的SurfaceView内容
问题:为什么SurfaceView不能截图?
- SurfaceView的实际渲染内容在独立的Surface中
- View.draw()只绘制了一个透明的洞(drawColor CLEAR)
- getDrawingCache()只能获取View hierarchy中的内容
- 要获取SurfaceView的内容,需要通过PixelCopy API(Android 7.0+)
问题:BufferQueue的三缓冲机制是怎么工作的?
- BufferQueue维护3个GraphicBuffer,状态在FREE/DEQUEUED/QUEUED/ACQUIRED间流转
- 生产者dequeue获取空闲Buffer绘制,完成后queue提交
- 消费者acquire获取已绘制Buffer进行合成,完成后release归还
- 三缓冲允许在消费者处理一帧时,生产者同时绘制下一帧,减少等待
问题:VSYNC信号的作用?
- 避免画面撕裂(tearing):确保Buffer交换与屏幕刷新同步
- 协调App绘制与SurfaceFlinger合成的时序
- VSYNC-app通知App开始绘制,VSYNC-sf通知SurfaceFlinger开始合成
- Choreographer接收VSYNC-app,驱动View的measure/layout/draw