2018年10月24日 17:20:08 可爱的you 阅读数:2077
 

在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面,由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程中进行行绘制,由于不占用主线程资源,SurfaceView一方面可以实现复杂而高效的UI。

SurfaceView的绘制方式效率非常高,因为SurfaceView的窗口刷新的时候不需要重绘应用程序的窗口(android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次,因此效率非常低下)。
View通过刷新来重绘视图,Android系统通过发出VSYNC信号来进行屏幕的重绘,刷新的时间间隔一般为16ms,在一些需要频繁刷新的界面,如果刷新执行很多逻辑绘制操作,就会导致刷新使用时间超过了16ms,就会导致丢帧或者卡顿,比如你更新画面的时间过长,那么你的主UI线程会被你的绘制函数阻塞,那么将无法响应按键,触屏等消息,会造成 ANR 问题,SurfaceView虽然继承自View,但拥有独立的surface,即它不与其宿主窗口共享同一个surface,可以单独在一个线程进行绘制,并不会占用主线程的资源,这样,绘制就会比较高效,游戏,视频播放,直播,都可以用SurfaceView来实现,SurfaceView有两个子类GLSurfaceView和VideoView

SurfaceView里面镶嵌的Surface是在包含SurfaceView的宿主Activity窗口(顶层视图对应的Surface)后面,用来描述SurfaceView的Layer的Z轴位置是小于用来描述其宿主Activity窗口的Layer的Z轴位置的,这样SurfaceView的Layer就被挡住看不见了,SurfaceView提供了一个可见区域,只有在这个可见区域内的surface部分内容才可见,就好像SurfaceView会在宿主Activity窗口上面挖一个“洞”出来,以便它的UI可以漏出来对用户可见,实际上,SurfaceView只不过是在其宿主Activity窗口上设置了一块透明区域

如上Activity窗口的顶层视图DecorView及其两个TextView控件都是通过窗口的Surface对应的Canvas绘制在SurfaceFlinger服务中的同一个Layer上,而SurfaceView的UI是通过其自己的Surface对应的Canvas绘制在SurfaceFlinger服务中的另外一个Layer上。

要了解 SurfaceView ,还须了解它的另外两个组件:Surface 和 SurfaceHolder,他们三者之间的关系实质上就是 MVC,Model就是数据模型的意思也就是这里的Surface;View即视图也就是这里的SurfaceView;SurfaceHolder很明显可以理解为Controller(控制器)。

在SurfaceView中你可以通过SurfaceHolder接口访问它内部的surface,而我们执行绘制的方法就是操作这个 Surface内部的Canvas,处理Canvas画的效果和动画,大小,像素等,getHolder()方法可以得到这个SurfaceHolder,通过SurfaceHolder来控制surface的尺寸和格式,或者修改监视surface的变化等等。

SurfaceHolder有三个回调方法可以监听SurfaceView中的surface的生命周期,SurfaceView一开始创建出来后,它拥有的Surface不一定会一起创建出来,SurfaceView变得可见时,surface被创建,SurfaceView隐藏前,surface被销毁,被创建了表示可以开始准备绘制了,而被销毁后我们要释放其他资源,Surfaceview一般会继承SurfaceHolder的Callback接口,SurfaceHolder.Callback具有如下的方法:
   surfaceCreated(SurfaceHolder holder):当Surface第一次创建后会立即调用该函数,可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在新线程来绘制界面,所以不要在这个函数中绘制Surface。 
   surfaceChanged(SurfaceHolder holder, int format, int width,int height):当Surface的状态(大小和格式)发生变化的时候会调用该函数,在surfaceCreated调用后该函数至少会被调用一次。 
   surfaceDestroyed(SurfaceHolder holder):当Surface被摧毁前会调用该函数,该函数被调用后就不能继续使用Surface了,一般在该函数中来清理使用的资源。

特别需要注意的是SurfaceView和SurfaceHolder.Callback的所有回调方法都是在主线程中回调的,在绘制前必须先合法的获取 Surface 才能开始绘制内容, SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed() 之间的状态为合法的,在这之外使用Surface都会出错。

在使用SurfaceView过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或则Canvas lockCanvas(Rect dirty)函数来锁定并且获取Surface中的Canvas画布对象,通过在Canvas上绘制内容来修改Surface中的数据,如果Surface被别的线程占有不可编辑或则尚未创建或者已经被销毁,调用该函数会返回null。

在unlockCanvas() 和 lockCanvas()之间Surface的内容是不缓存的,所以需要完全重绘Surface的内容,如果为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect dirty)函数来指定一个dirty区域,这样该区域外的内容会缓存起来,只更新需要重绘的区域,相对部分内存要求比较高的游戏来说,不重画dirty外的其他区域的像素,可以提高速度。

在调用lockCanvas函数获取Surface的Canvas后,SurfaceView会利用Surface的一个同步锁锁住画布Canvas,直到调用unlockCanvasAndPost(Canvas canvas)函数,才解锁画布并提交改变,将图形显示,这里的同步机制保证Surface的Canvas在绘制过程中不会被改变(被摧毁、修改),避免多个不同的线程同时操作同一个Canvas对象。

双缓冲:SurfaceView在更新视图时用了两个Canvas,一张frontCanvas和一张backCanvas,每次实际显示的是frontCanvas,backCanvas存储的是上一次更改前的视图,当使用lockCanvas()获取画布时,得到的实际上是backCanvas而不是正在显示的frontCanvas,当你在获取到的backCanvas上绘制完成后,再使用unlockCanvasAndPost(canvas)提交backCanvas视图,那么这张backCanvas将替换正在显示的frontCanvas被显示出来,原来的frontCanvas将切换到后台作为backCanvas,这样做的好处是在绘制期间不会出现黑屏。

SurfaceView类的成员变量mRequestedType描述的是SurfaceView的绘图表面Surface的类型,一般来说,它的值可能等于SURFACE_TYPE_NORMAL或者SURFACE_TYPE_PUSH_BUFFERS,
当一个SurfaceView的绘图表面的类型等于SURFACE_TYPE_NORMAL的时候,就表示该SurfaceView的绘图表面所使用的内存是一块普通的内存,一般来说,这块内存是由SurfaceFlinger服务来分配的,我们可以在应用程序内部自由地访问它,即可以在它上面填充任意的UI数据,然后交给SurfaceFlinger服务来合成,并且显示在屏幕上,在这种情况下,在SurfaceFlinger服务一端使用一个Layer对象来描述该SurfaceView的绘图表面。

当一个SurfaceView的绘图表面的类型等于SURFACE_TYPE_PUSH_BUFFERS的时候,就表示该SurfaceView的绘图表面所使用的内存不是由SurfaceFlinger服务分配的,我们不能够在应用程序内部对它进行操作,所以不能调用lockCanvas来获取Canvas对象进行绘制了,例如当一个SurfaceView是用来显示摄像头预览或者视频播放的时候,我们就会将它的绘图表面的类型设置为SURFACE_TYPE_PUSH_BUFFERS,这样摄像头服务或者视频播放服务就会为该SurfaceView绘图表面创建一块内存,并且将采集的预览图像数据或者视频帧数据源源不断地填充到该内存中去,在这种情况下,在SurfaceFlinger服务一端使用一个LayerBuffer对象来描述该SurfaceView的绘图表面。
所以:决定surfaceView的内存是普通内存(由开发者自己决定用来绘制什么)还是专用的内存(显示摄像头或者视频等,开发者无法使用这块内存)由mRequestType决定,我们在创建了一个SurfaceView之后,可以调用它的SurfaceHolder对象的成员函数setType来修改该SurfaceView的绘图表面的类型,绘图表面类型为SURFACE_TYPE_PUSH_BUFFERS的SurfaceView的UI是不能由应用程序来控制的,而是由专门的服务来控制的,例如,摄像头服务或者视频播放服务。
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用到的数据由其他对象提供。

SurfaceView 与view区别详解的更多相关文章

  1. Android中Intent传值与Bundle传值的区别详解

    Android中Intent传值与Bundle传值的区别详解 举个例子我现在要从A界面跳转到B界面或者C界面   这样的话 我就需要写2个Intent如果你还要涉及的传值的话 你的Intent就要写两 ...

  2. View绘制详解(二),从setContentView谈起

    掐指一算,本来今天该介绍View的测量了,可是要说View的测量,那就要从setContentView谈起了,setContentView本身涉及到的东西也是挺多的,所以今天我们就先来看看这个setC ...

  3. View绘制详解,从LayoutInflater谈起

    自定义View算是Android开发中的重中之重了,很多小伙伴可能或多或少都玩过自定义View,对View的绘制流程也有一定的理解.那么现在我想通过几篇博客来详细介绍View的绘制流程,以便使我们更加 ...

  4. laravel 配置路由 api和web定义的路由的区别详解

    1.路由经过中间件方面不同 打开kerenl.php就可以看到区别 protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware ...

  5. 基于Java的打包jar、war、ear包的作用与区别详解

      本篇文章,小编为大家介绍,基于Java的打包jar.war.ear包的作用与区别详解.需要的朋友参考下   以最终客户的角度来看,JAR文件就是一种封装,他们不需要知道jar文件中有多少个.cla ...

  6. php 去除html标记--strip_tags与htmlspecialchars的区别详解

    php 去除html标记--strip_tags与htmlspecialchars的区别详解 作者: 字体:[增加 减小] 类型:转载 时间:2013-06-26   本篇文章是对php中去除html ...

  7. HTTP POST GET 本质区别详解

    HTTP POST GET 本质区别详解 一 原理区别 一般在浏览器中输入网址访问资源都是通过GET方式:在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认为GET提交 Ht ...

  8. javascript中=、==、===区别详解

    javascript中=.==.===区别详解今天在项目开发过中发现在一个小问题.在判断n==""结果当n=0时 n==""结果也返回了true.虽然是个小问题 ...

  9. View绘制详解(五),draw方法细节详解之View的滚动/滑动问题

    关于View绘制系列的文章已经完成了四篇了,前面四篇文章主要带小伙伴们熟悉一下View的体系的整体框架.View的测量以及布局等过程,从本篇博客开始,我们就来看看View的绘制过程.View的绘制涉及 ...

随机推荐

  1. C++用rand()和srand()生成随机数

    内容来自<编程实战宝典> 首先来看函数原型 int rand(void); void srand(unsigned int seed); 1.rand()函数不需要任何参数,直接返回一个随 ...

  2. ElasticSearch基础入门学习笔记

    前言 本笔记的内容主要是在从0开始学习ElasticSearch中,按照官方文档以及自己的一些测试的过程. 安装 由于是初学者,按照官方文档安装即可.前面ELK入门使用主要就是讲述了安装过程,这里不再 ...

  3. 2019全国大学生信息安全大赛两道web

    简单小结 菜鸟第一次打国赛,这次题目质量很高,学到了许多姿势. Web Justsoso 打开题目,源代码出存在提示: 使用LFI读取index.php与hint.php http://d4dc224 ...

  4. Python原来这么好学-2.1节: 选择PyCharm作为开发工具

    这是一本教同学们彻底学通Python的高质量学习教程,认真地学习每一章节的内容,每天只需学好一节,帮助你成为一名卓越的Python程序员: 本教程面向的是零编程基础的同学,非科班人士,以及有一定编程水 ...

  5. tomcat 安装在 linux

    简单说下什么是tomcat?它与apache web服务器的关系? Apache是web服务器(静态解析,如HTML),tomcat是java应用服务器(动态解析,如JSP.PHP) Tomcat只是 ...

  6. 使用ASP.NET Core构建RESTful API的技术指南

    译者荐语:利用周末的时间,本人拜读了长沙.NET技术社区翻译的技术标准<微软RESTFul API指南>,打算按照步骤写一个完整的教程,后来无意中看到了这篇文章,与我要写的主题有不少相似之 ...

  7. 量化投资学习笔记27——《Python机器学习应用》课程笔记01

    北京理工大学在线课程: http://www.icourse163.org/course/BIT-1001872001 机器学习分类 监督学习 无监督学习 半监督学习 强化学习 深度学习 Scikit ...

  8. Linux学习小记(2)---installation

    安装一方面需要注意镜像的定位问题,另一方面是在我的双显卡笔记本上,总是进不去安装界面和启动失败,我的原因是驱动问题,需要在启动参数加上nouveau.mideset=0,或者nomodeset等指令来 ...

  9. 【HDU - 1260 】Tickets (简单dp)

    Tickets 搬中文 Descriptions: 现在有n个人要买电影票,如果知道每个人单独买票花费的时间,还有和前一个人一起买花费的时间,问最少花多长时间可以全部买完票. Input 给出 N(1 ...

  10. 图解Java设计模式之UML类图

    图解Java设计模式之UML类图 3.1 UML基本介绍 UML图 UML类图 3.1 UML基本介绍 1)UML – Unified modeling language UML(统一建模语言),是一 ...