基础理解
SurfaceView
内嵌的Surface
是在包含有SurfaceView
的Activity窗口的后面
, 用描述SurfaceView
的Layer[图层]的Z轴位置
小于 用来描述Activity窗口的Layer的Z轴位置
,因此SurfaceView
是不可见的, 但SurfaceView
提供了一个可见区域,只有在此可见区域内Surface
部分内容才可见,SurfaceView
在界面上的表现就像在Activity窗口挖了一个“黑洞”
,以便显示它的UI
。实际上就相当于在Activity窗口
设置了一块透明区域
SurfaceViiew 与 View的区别
- [1]
SurfaceView
拥有独立的绘图表面,因此SurfaceView
的UI
可以在一个独立的线程中进行绘制,不占用主线程资源,可以实现复杂而高效的UI
绘制,在窗口刷新是不需要重绘应用程序窗口- [2]
View
通过刷新来重绘视图,Android系统通过发出VSYNC信号
来进行屏幕重绘,刷新间隔16ms,超过16ms则会导致丢帧或卡顿,此时的UI
线程会被绘制函数阻塞导致无法相应造成ANR
- [3]
SurfaceView
虽继承与View却拥有独立surface
,不同应用窗口共享一个surface
,不占用主线程的资源单独在一个线程进行绘制,因此比较高效。一般视频播放、直播、游戏都可以用SurfaceView
实现
使用详解
SurfaceView子类
GLSurfaceView
VideoView
SurfaceView中的MVC
若要使用SurfaceView
我们还需了解它的两个组件来共同控制SurfaceView
的展示,组成了一个完整的MVC模式
- Surface.java 用于存放数据模型
- SurfaceHolder.java 作为控制器的存在
- SurfaceView.java 用来显示视图
SurfaceHolder
SurfaceView
有三个回调方法可以监听SurfaceView
中surface
生命周期,SurfaceView
被创建出来后他拥有的surface
不一定会被创建出来
SurfaceView
变得可见 >>>surface
被创建 >>> 可以开始绘制SurfaceView
隐藏前 >>>surface
被销毁 >>> 释放其他资源
SurfaceView
一般继承SurfaceHolder
的Callback接口
,具体监控方法如下:
surfaceCreated(SurfaceHolder holder) 函数
:第一次创建立即调用,做绘制界面的相关初始化操作,一般都是在新线程中绘制界面,不要放在此函数中绘制surface
surfaceChanged(SurfaceHolder holder, int format, int width,int height) 函数
:当surface
状态发生变化时调用,状态发生变化的范围包括 大小和格式surfaceDestroyed(SurfaceHolder holder) 函数
: 当surface被摧毁前调用,调用后不能继续使用surface
,一般用来清理使用资源
双缓存
SurfaceView
在更新视图时用了两个Canvas
,一张frontCanvas
画布 和一张backCanvas
画布,每次实际上用于显示的是frontCanvas
画布,而backCanvas
画布用于存储上次更改前的视图;
当使用lockCanvas
函数 获取画布时,我们得到的实际是backCanvas
而不是正在显示的frontCanvas
,当获取到的backCanvas
上绘制完使用unlockCanvasAndPost(Canvas canvas)
提交backCanvas
视图后,那么这张backCanvas
将替代当前正在显示的frontCanvas
,进而被展示出来;原来的frontCanvas
会被切换到后台作为backCanvas
存在。这样可以保证在绘制期间不会出现黑屏
SurfaceView
类的成员变量mRequestedType
用来决定绘制表面Surface的类型和内存的选择
- SURFACE_TYPE_NORMAL
- SURFACE_TYPE_BUFFERS
[1] SURFACE_TYPE_NORMAL
[1] 表示
SurfaceView
绘图表面所使用的内存是一块 普通内存
[2] 此内存由SufaceFlinger服务分配
,应用程序内存自由访问它,可在它上面填充任意的UI
数据,然后交由SurfaceFlinger服务
来合成,显示在屏幕上
[3]SurfaceFlinger服务
一段使用一个Layer对象来描述该SurfaceView
的绘图表面
[2] SURFACE_TYPE_BUFFERS
[1] 表示
SurfaceView
绘图表面所使用的内存不是由SurfaceFlinger服务
分配的,应用程序内部不能对它进行操作,所以不能调用lockCanvas
来获取Canvas
对象进行绘制
[2] 使用场景一般用于使用摄像头服务或视频播放服务,摄像头服务或视频服务会为该SurfaceView
绘图表面创建一块内存,并将采集的预览图像数据或视频帧数据源源不断的填充到内存中,在SurfaceFlinger服务端
使用LayerBuffer
对象来描述该SurfaceView
的绘图表面.
SurfaceView
类的成员变量mRequestedType
目前接收如下的参数:
SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通
Surface
SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )
引擎和硬件加速的Surface
SURFACE_TYPE_GPU:适用于GPU
加速的Surface
SURFACE_TYPE_PUSH_BUFFERS:表明该Surface
不包含原生数据,Surface
用到的数据由其他对象提供。
特别注意:
- [1] 所有回调都是在 主线程 中执行的,绘制前必须获取合法的
Surface
才开始绘制,保证surfaceCreated函数
和surfaceDestoryed函数
之间的状态为合法的- [2]
SurfaceView
不直接与Surface
打交道,交由SurfaceHolder
的Canvas
lockCanvas函数
或lockCanvas(Rect dirty)函数
来锁定并获取Surface
中的Canvas画布
对象- [3] 通过在
Canvas
上绘制内容来修改Surface
中的数据,若Surface
被别的线程占用不可编辑 或 未创建 或 已被销毁,调用此函数返回null
在unlockCanvas函数
和lockCanvas函数
之间的Surface
的内容是不进行缓存的,所以需要完全重绘Surface
的内容- [4] 若想提高效率只绘制变化部分的内容则可以调用
lockCanvas(Rect dirty)函数
来实现绘制指定的dirty
区域,这样区域外的内容会缓存起来,只对重绘区域作更新- [5] 在调用
lockCanvas函数
获取Surface
的Canvas
后,SurfaceView
会利用Surface
的同步锁锁住画布Canvas
,直到调用unlockCanvasAndPost(Canvas canvas)函数
后才解锁画布并提交改变,将图形显示;这里的同步机制保证Surface
的Canvas
在绘制过程中不会被改变(被销毁、修改),避免多个不同的线程同时操作一个Canvas对象