SurfaceView 与view区别详解
SurfaceView 与view区别详解
在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区别详解的更多相关文章
- Android中Intent传值与Bundle传值的区别详解
Android中Intent传值与Bundle传值的区别详解 举个例子我现在要从A界面跳转到B界面或者C界面 这样的话 我就需要写2个Intent如果你还要涉及的传值的话 你的Intent就要写两 ...
- View绘制详解(二),从setContentView谈起
掐指一算,本来今天该介绍View的测量了,可是要说View的测量,那就要从setContentView谈起了,setContentView本身涉及到的东西也是挺多的,所以今天我们就先来看看这个setC ...
- View绘制详解,从LayoutInflater谈起
自定义View算是Android开发中的重中之重了,很多小伙伴可能或多或少都玩过自定义View,对View的绘制流程也有一定的理解.那么现在我想通过几篇博客来详细介绍View的绘制流程,以便使我们更加 ...
- laravel 配置路由 api和web定义的路由的区别详解
1.路由经过中间件方面不同 打开kerenl.php就可以看到区别 protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware ...
- 基于Java的打包jar、war、ear包的作用与区别详解
本篇文章,小编为大家介绍,基于Java的打包jar.war.ear包的作用与区别详解.需要的朋友参考下 以最终客户的角度来看,JAR文件就是一种封装,他们不需要知道jar文件中有多少个.cla ...
- php 去除html标记--strip_tags与htmlspecialchars的区别详解
php 去除html标记--strip_tags与htmlspecialchars的区别详解 作者: 字体:[增加 减小] 类型:转载 时间:2013-06-26 本篇文章是对php中去除html ...
- HTTP POST GET 本质区别详解
HTTP POST GET 本质区别详解 一 原理区别 一般在浏览器中输入网址访问资源都是通过GET方式:在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认为GET提交 Ht ...
- javascript中=、==、===区别详解
javascript中=.==.===区别详解今天在项目开发过中发现在一个小问题.在判断n==""结果当n=0时 n==""结果也返回了true.虽然是个小问题 ...
- View绘制详解(五),draw方法细节详解之View的滚动/滑动问题
关于View绘制系列的文章已经完成了四篇了,前面四篇文章主要带小伙伴们熟悉一下View的体系的整体框架.View的测量以及布局等过程,从本篇博客开始,我们就来看看View的绘制过程.View的绘制涉及 ...
随机推荐
- 仅仅知道如何终止XHR请求,或许对你来说是不够的!
TLDR: 当我们需要的时候,我们可以通过AbortController接口来终止一个或者多个请求. 前言 到目前为止,我们有两个常用的基本的手段去发送请求进而局部刷新页面内容,其一是XMR(XMLH ...
- C#的静态工厂方法与构造函数对比
最近,在与同事进行协同编程时,我们开始讨论在C#中初始化新对象的最佳方法.我一直是使用构造函数实现,尽管他倾向于静态工程方法.这引起了关于每种类型的利弊的大量来来回回的讨论. 为了说明我所说的内容,这 ...
- android系统webview使用input实现选择文件并预览
一般系统的实现方式: 代码实现 <!doctype html> <html> <head> <meta charset="utf-8"&g ...
- 深入理解 Android 中的各种 Context
前言 网上关于 Context 的文章也已经有不少了,比如值得参考的有: Android Context完全解析,你所不知道的Context的各种细节 Android Context 到底是什么? 但 ...
- MySQL索引优化深入
创建 test 测试表 CREATE TABLE `test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `c1` varchar(10) DEFAULT N ...
- springboot~HttpPut开启application/x-www-form-urlencoded
在使用spring框架时,默认情况下@RequestParam注解只到接受Get和Post请求参数 ,而对于Put来说默认是使用@ReqeustBody注解的,如果希望为Put也开启@RequestP ...
- 阿里云服务器Web Deploy配置和使用Visual Studio进行Web项目发布部署遇到的坑
阿里云的服务器一直闲着,烧着银子,当初花几千大洋开通,本想弄信息化的项目为所帮扶的贫困户脱贫助手,不想势单力薄,一直没有找到好的项目.最近大家都在众志成城抗击新肺疫情,于是又想能不能尽点自己的力量,于 ...
- 面向对象+闭包+三种对象的声明方式(字面式、new Object、构造函数、工厂模式、原型模式、混合模式)
面向对象: 对代码的一种抽象,对外统一提供调用接口的编程思想 对象的属性:事物自身拥有的东西 对象的方法:事物的功能 对象:事物的一个实例 对象的原型:.prototype -> 内存地址 -& ...
- 【seata源码学习】001 - seata-server的配置读取和服务注册
github, seata vergilyn seata-fork seata.io zh-cn docs (PS. 随缘看心情写,坚持不了几天.文章还是写的超级的烂,排版也奇差无比~~~~ 脑壳疼~ ...
- 【48】数据扩充(Data augmentation)
数据扩充(Data augmentation) 大部分的计算机视觉任务使用很多的数据,所以数据扩充是经常使用的一种技巧来提高计算机视觉系统的表现.我认为计算机视觉是一个相当复杂的工作,你需要输入图像的 ...