编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

    • 库的解读

    • 专栏博客

      • 系统启动Zygote
      • Binder通信原理
      • Handler消息机制
      • Activity启动原理
      • 四大组件原理分析
      • AMS与组件管理
      • View绑制与渲染
      • 事件分发机制
      • 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 面试高频问题
      • 自定义View设计
      • WMS窗口管理
      • PMS与APK安装
      • 虚拟机与类加载
      • 内存管理与GC
      • 线程与并发编程
      • 性能优化与监控
      • 序列化与数据存储
      • 组件化与路由设计
      • 插件化与热修复
      • NDK开发实践
      • WebView核心设计
      • ADB常见使用操作
    • 智能硬件

  • iOS开发和进阶

  • Web开发和进阶

  • Linux应用开发

  • Apps
  • Android提升进阶
  • 专栏博客
杨充
2026-04-14
目录

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单独设置帧率或缓冲策略
1
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
1
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(合成送显)
                      ↓
                   显示屏
1
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上绘制,减少等待
1
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处理能力
1
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   (提交)
1
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(独立的绘制数据)
1
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

流程解析:

  1. SurfaceView向父视图逐层请求透明区域,根视图通过Binder通知WMS将对应区域设为透明
  2. 建立与独立绘图表面的连接(SurfaceSession),创建控制器(SurfaceControl)
  3. 后续可与独立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上方)
1
2
3
4
5
6
7
  • setZOrderOnTop(true):将SurfaceView放到Activity上方
  • setZOrderMediaOverlay(true):用于字幕场景,在视频层之上但Activity之下

# 4.5 SurfaceView为何不卡顿

两方面保障:

  1. 双缓冲技术:使用frontCanvas和backCanvas,绘制在backCanvas上完成后与frontCanvas交换,避免屏幕闪烁
  2. 独立线程绘制:在子线程更新UI,不阻塞主线程,保证输入事件响应速度

双缓冲工作原理:

双缓冲机制:

lockCanvas() → 获取backCanvas → 在backCanvas上绘制
→ unlockCanvasAndPost() → backCanvas变为frontCanvas显示
→ 原frontCanvas变为backCanvas → 下次绘制使用
1
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合成
    → 送显
1
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前解绑
1
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是空实现
}
1
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中正常渲染
    ↓
屏幕显示
1
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()
1
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保护的内容
1
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:

  1. Android 7.0+系统,SurfaceView性能优势明显
  2. 不需要动画效果的视频播放
  3. 游戏渲染等高帧率场景
  4. 对内存和功耗敏感的场景
SurfaceView优先的详细理由:

Android 7.0+的SurfaceView改进:
  → 支持同步移动(SurfaceView随父View同步滚动)
  → 大大减少了画面不同步问题
  → 成为Google官方推荐的视频/游戏渲染方案

性能优势量化:
  → 少一次GPU合成操作 → 省约2ms/帧
  → 不占用主线程 → 主线程掉帧不影响视频流畅度
  → 独立BufferQueue → Buffer管理更高效
  → 内存占用比TextureView低约30%

ExoPlayer/MediaPlayer推荐:
  → Google的ExoPlayer默认使用SurfaceView
  → DRM安全内容必须使用SurfaceView(TextureView无法显示受保护内容)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 7.2 选择TextureView的场景

  1. 7.0以下系统需要动画效果时
  2. 需要截图功能
  3. 需要在ScrollView等滑动容器中使用
  4. 需要进行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转场动画
1
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(性能更优)
1
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内存) │           │ (等待显示)│           │          │
   └──────────┘            └──────────┘            └──────────┘
1
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软件解码,兜底)
1
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状态机(着色器、纹理等)
1
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();
1
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 → 合成送显
1
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
上次更新: 2026/06/10, 11:13:41
事件分发机制
自定义View设计

← 事件分发机制 自定义View设计→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式