SurfaceView介绍

  Android 音视频开发中,SurfaceView 是经常被用到的视频渲染的 View。我们知道大多数的 android 视图渲染类都是 View 的子类,没接触视频开发前,常用的 View 有布局组件 ConstraintLayout、RelativeLayout 与基本的显示组件ImageView、TextView 等。那么为什么视频画面使用 SurfaceView 而不用之前用到的这些 View 渲染呢?今天通过介绍 SurfaceView 来探讨这个问题。

Android 开发者文档对 SurfaceView 的介绍如下:

Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen。

SurfaceView 提供嵌入视图层次结构内部的专用绘图表面(Surface)。可以控制此表面的格式,也可以控制其大小;SurfaceView 负责将表面(Surface)放置在屏幕上的正确位置。

  听起来 SurfaceView 像是为 Surface 服务的,那么 Surface 又是什么?Android 开发者文档对 Surface 的介绍如下:

Handle onto a raw buffer that is being managed by the screen compositor.

A Surface is generally created by or from a consumer of image buffers (such as a SurfaceTexture, MediaRecorder, or Allocation), and is handed to some kind of producer (such as OpenGL, MediaPlayer, or CameraDevice) to draw into.

Surface 用来处理由屏幕合成器管理的原始缓冲区。Surface通常由图像缓冲区的使用者(例如SurfaceTexture,MediaRecorder或Allocation)创建或由其创建,并交给某种类型的生产者(例如OpenGL,MediaPlayer或CameraDevice)进行绘制。Surface 继承自 object, 实现了Parcelable 接口。

  Android 平台上,无论开发者使用什么渲染 API,一切内容都会渲染到 Surface。Surface 表示缓冲队列中的生产方,在 Android 平台上创建的每个窗口都由 Surface 提供支持。

  到这里我们了解到 Surface 是 Android 平台渲染图像的基本要素。Android 的视图结构是树形结构,因为Surface 继承自 object,不是View的子类,所以不能直接被放置到 view 树中,如果想在 view 树中放置 Surface, 就可以使用 SurfaceView。简单一些可以理解为 SurfaceView 是对 Surface 的包装,用来向 View 树提供 Surface 的一个 View。

  知道了 Surface 和 SurfaceView 的职责以后,下面通过对比 SurfaceView 和普通 View 来加深对 SurfaceView 的理解:

  1. View 在主线程中对页面进行刷新,而 SurfaceView 开启一个子线程对页面进行刷新;
  2. View 在绘图时没有实现双缓冲机制,SurfaceView 在底层机制中就实现了双缓冲机制;
  3. View 适用于主动更新的情况,而 SurfaceView 适用于被动更新的情况,比如频繁刷新界面。

  双缓冲技术是游戏开发中的一个重要的技术。当一个个画面争先显示时,程序又在改变它,前面的画面还没有显示完,程序又请求重新绘制,这样屏幕就会不停地闪烁。双缓冲技术是把要处理的图片在内存中处理好之后,再将其显示在屏幕上。双缓冲主要是为了解决反复局部刷屏带来的闪烁。把要画的东西先画到一个内存区域里,然后整体的一次性画出来。

  View 的 onDraw 方法是运行在主线程中的,如果 onDraw 方法中执行的操作比较耗时,会轻微阻塞主线程,对于需要频繁刷新页面的场景,如果 onDraw 方法执行的时间过长,会导致以下情况:

  1. 掉帧,出现页面卡顿,而SurfaceView采用了双缓冲技术,提高了绘制的速度,可以缓解这一现象;
  2. 用户事件的响应受到影响,也就是响应速度下降,影响了用户的体验。SurfaceView 可以在子线程中更新UI,不会阻塞主线程,提高了响应速度。

  SurfaceView 的工作方式是创建一个置于应用窗口之后的新窗口。这种方式的效率非常高,因为 SurfaceView 窗口刷新的时候不需要重绘应用程序的窗口(android 普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次,因此效率相对低下,不过满足普通应用界面的需求还是绰绰有余)。

  但是这么做也有一些非常不便的限制。因为 SurfaceView 的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView 或者 ScrollView中,不能使用UI控件的一些特性比如 View.setAlpha()。为了解决这个问题 Android 4.0中引入了 TextureView。它们都可以在另一个独立线程中绘制和渲染,这是它们和其它View的最大不同。

  与 SurfaceView 相比,TextureView 并没有创建一个单独的 Surface(表面)用来绘制,这使得它可以像一般的 View 一样执行一些变换操作,设置透明度等。但是 Textureview 必须在硬件加速开启的窗口中使用,而且 TextureView 使用也更加耗电,平均 TextureView 比 SurfaceView 会多用30%的电量。

  对 SurfaceView 的简单介绍就到这里,现在再来看开篇的问题:为什么视频画面的渲染使用 SurfaceView 而不用之前用到的普通 View?答案的要点已经在文章中阐述。