SurfaceView详解

作者: AntCoding | 来源:发表于2019-04-13 18:53 被阅读111次

基础理解

SurfaceView内嵌的Surface是在包含有SurfaceViewActivity窗口的后面, 用描述SurfaceViewLayer[图层]的Z轴位置 小于 用来描述Activity窗口的Layer的Z轴位置,因此SurfaceView是不可见的, SurfaceView提供了一个可见区域,只有在此可见区域内Surface部分内容才可见,SurfaceView在界面上的表现就像在Activity窗口挖了一个“黑洞”,以便显示它的UI。实际上就相当于在Activity窗口设置了一块透明区域

SurfaceViiew 与 View的区别

  • [1] SurfaceView拥有独立的绘图表面,因此 SurfaceViewUI可以在一个独立的线程中进行绘制,不占用主线程资源,可以实现复杂而高效的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有三个回调方法可以监听SurfaceViewsurface生命周期,SurfaceView被创建出来后他拥有的surface不一定会被创建出来

  • SurfaceView变得可见 >>> surface被创建 >>> 可以开始绘制
  • SurfaceView隐藏前 >>> surface被销毁 >>> 释放其他资源

SurfaceView一般继承SurfaceHolderCallback接口,具体监控方法如下:

  • 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打交道,交由SurfaceHolderCanvas lockCanvas函数lockCanvas(Rect dirty)函数来锁定并获取Surface中的Canvas画布对象
  • [3] 通过在Canvas上绘制内容来修改Surface中的数据,若Surface被别的线程占用不可编辑 或 未创建 或 已被销毁,调用此函数返回 null
    unlockCanvas函数lockCanvas函数之间的Surface的内容是不进行缓存的,所以需要完全重绘Surface的内容
  • [4] 若想提高效率只绘制变化部分的内容则可以调用lockCanvas(Rect dirty)函数来实现绘制指定的dirty区域,这样区域外的内容会缓存起来,只对重绘区域作更新
  • [5] 在调用lockCanvas函数获取SurfaceCanvas后,SurfaceView会利用Surface的同步锁锁住画布Canvas,直到调用unlockCanvasAndPost(Canvas canvas)函数后才解锁画布并提交改变,将图形显示;这里的同步机制保证SurfaceCanvas在绘制过程中不会被改变(被销毁、修改),避免多个不同的线程同时操作一个Canvas对象

This ALL! Thanks EveryBody!

相关文章

网友评论

    本文标题:SurfaceView详解

    本文链接:https://www.haomeiwen.com/subject/mqjsiqtx.html