走进SurfaceView

作者:康白

博客:http://blog.csdn.net/k_bb_666

最近在写视频播放器的时候用到了SurfaceView和MediaPlayer,在各个功能完成后,竟得意忘形,感觉自己又get到新技能,可以嘚瑟几天了,直到前两天被人问到:

问“SurfaceView和Surface有什么区别啊?它们是一个东西吗?” 答“当然不是啊,肯定不是啊…SurfaceView就是一个View啊…Surface是…” …… 紧接着脑海里就响起了我五月天的那首歌…“最怕空气突然安静…” 真是大写的尴尬啊,我想,是时候好好研究一下SurfaceView,顺便总结总结了!

01

Surface是什么

Surface就是“表面”的意思。在SDK的文档中,对Surface的描述是这样的:“Handle onto a raw buffer that is being managed by the screen compositor”,翻译成中文就是“由屏幕显示内容合成器(screen compositor)所管理的原生缓冲器的句柄”,这句话包括下面两个意思:

  1. 通过Surface(因为Surface是句柄)就可以获得原生缓冲器以及其中的内容。就像在C语言中,可以通过一个文件的句柄,就可以获得文件的内容一样;
  2. 原生缓冲器(rawbuffer)是用于保存当前窗口的像素数据的。

引伸地,可以认为Android中的Surface就是一个用来画图形(graphics)或图像(image)的地方。根据Java方面的常规知识,我们知道通常画图是在一个Canvas对象上面进行的,由此,可以推知一个Surface对象中应该包含有一个Canvas对象

02

SurfaceView是什么

SurfaceView是View的派生类,因此它本质上是一个View。但与普通View不同的是,它有自己的Surface ,在WMS中有对应的WindowState,在SurfaceFlinger中有Layer。但只有通过SurfaceView才可以看到Surface的部分或者全部的内容。

我们知道,一般的Activity包含的多个View会组成View hierachy的树形结构,只有最顶层的DecorView,也就是根结点视图,才是对WMS可见的。这个DecorView在WMS中有一个对应的WindowState。相应地,在SurfaceFlinger中对应的Layer。

而SurfaceView自带一个Surface,这个Surface在WMS中有自己对应的WindowState,在SurfaceFlinger中也会有自己的Layer。虽然在App端它仍在View hierachy中,但在Server端(WMS和SurfaceFlinger)中,它与宿主窗口是分离的。这样的好处是对这个Surface的渲染可以放到单独线程去做,渲染时可以有自己的GL context。这对于一些游戏、视频等性能相关的应用非常有益,因为它不会影响主线程对事件的响应。

但它也有缺点,因为这个Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中,一些View中的特性也无法使用。在Android中Surface是从Object派生而来,且实现了Parcelable接口。看到Parcelable就让人能很自然地想到数据容器,SurfaceView就是用来展示Surface中的数据的。在这个层面上而言,Surface就是管理数据的地方,SurfaceView就是展示数据的地方。

03

SurfaceHolder

SurfaceHolder是一个接口,其作用就像一个关于Surface的监听器。提供访问和控制SurfaceView背后的Surface 相关的方法 (providing access and control over this SurfaceView’s underlying surface),它通过三个回调方法,让我们可以感知到Surface的创建、销毁或者改变。在SurfaceView中有一个方法getHolder,可以很方便地获得SurfaceView所对应的Surface所对应的SurfaceHolder。

除下面将要提到的SurfaceHolder.Callback外,SurfaceHolder还提供了很多重要的方法,其中最重要的就是:

  1. abstract void addCallback(SurfaceHolder.Callbackcallback) 为SurfaceHolder添加一个SurfaceHolder.Callback回调接口。
  2. abstract Canvas lockCanvas() 获取一个Canvas对象,并锁定之。所得到的Canvas对象,其实就是Surface中一个成员。
  3. abstract Canvas lockCanvas(Rectdirty) 同上。但只锁定dirty所指定的矩形区域,因此效率更高。
  4. abstract void unlockCanvasAndPost(Canvascanvas) 当修改Surface中的数据完成后,释放同步锁,并提交改变,然后将新的数据进行展示,同时Surface中相关数据会被丢失。
  5. public abstract void setType (int type) 设置Surface的类型,接收如下的参数: SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface SURFACE_TYPE_GPU:适用于GPU加速的Surface SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。需要注意的是,在高版本的Android SDK中,setType这个方法已经被depreciated了。

2、3、4中的同步锁机制的目的,就是为了在绘制的过程中,Surface中的数据不会被改变。

04

SurfaceHolder.Callback

前面已经讲到SurfaceHolder是一个接口,它通过回调方法的方式,让我们可以感知到Surface的创建、销毁或者改变。其实这一点是通过其内部的静态子接口SurfaceHolder.Callback来实现的。SurfaceHolder.Callback中定义了三个接口方法:

  1. abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 当surface发生任何结构性的变化时(格式或者大小),该方法就会被立即调用。
  2. abstract void surfaceCreated(SurfaceHolder holder) 当surface对象创建后,该方法就会被立即调用。
  3. abstract void surfaceDestroyed(SurfaceHolder holder) 当surface对象在将要销毁前,该方法会被立即调用。

05

它们之间的关系

从设计模式的高度来看,Surface、SurfaceView和SurfaceHolder实质上就是广为人知的MVC,即Model-View-Controller。Model就是模型的意思,或者说是数据模型,或者更简单地说就是数据,也就是这里的Surface;View即视图,代表用户交互界面,也就是这里的SurfaceView;SurfaceHolder很明显可以理解为MVC中的Controller(控制器)。而SurfaceHolder.Callback是SurfaceHolder内部的静态子接口

06

SurfaceView的优缺点

1、优点: 可以在一个独立的线程中进行界面绘制,不会影响主线程,使用双缓冲机制,播放视频时画面更流畅。SurfaceView内部自己持有surface,surface 创建、销毁、大小改变是系统来处理的,通过surfaceHolder 的callback回调通知。当画布创建好时,可以将surface绑定到MediaPlayer中。SurfaceView如果为用户可见的时候,创建SurfaceView的SurfaceHolder用于显示视频流解析的帧图片,如果发现SurfaceView变为用户不可见的时候,则立即销毁SurfaceView的SurfaceHolder,以达到节约系统资源的目的

2、缺点: Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中。SurfaceView 不能嵌套使用。

07

SurfaceView双缓冲

双缓冲:在运用时可以理解为:SurfaceView在更新视图时用到了两张Canvas,一张frontCanvas和一张backCanvas,每次实际显示的是frontCanvas,backCanvas存储的是上一次更改前的视图,当使用lockCanvas()获取画布时,得到的实际上是backCanvas而不是正在显示的frontCanvas,之后你在获取到的backCanvas上绘制新视图,再unlockCanvasAndPost(canvas)此视图,那么上传的这张canvas将替换原来的frontCanvas作为新的frontCanvas,原来的frontCanvas将切换到后台作为backCanvas。例如,如果你已经先后两次绘制了视图A和B,那么你再调用lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你将重绘的C视图上传,那么C将取代B作为新的frontCanvas显示在SurfaceView上,原来的B则转换为backCanvas。

原文发布于微信公众号 - Android机动车(JsAndroidClub)

原文发表时间:2017-09-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android干货

Android多媒体录制--MediaRecorder视频录制

4337
来自专栏*坤的Blog

博客园特效简单添加

2324
来自专栏游戏杂谈

React Native入门遇到的一些问题

curl -LsSf http://github.com/mxcl/homebrew/tarball/master | sudo tar xvz -C/usr/...

1674
来自专栏逆向技术

win32程序之子窗口编程

  在前边我们已经讲解了窗口的本质.以及如何注册窗口类跟创建窗口. 还讲了消息循环.

2092
来自专栏Danny的专栏

ASP.NET中的几种分页

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

1172
来自专栏Jaycekon

Phantomjs+Nodejs+Mysql数据抓取(2.抓取图片)

概要 这篇博客是在上一篇博客Phantomjs+Nodejs+Mysql数据抓取(1.抓取数据) http://blog.csdn.net/jokerko...

3545
来自专栏iOS122-移动混合开发研究院

SVProgressHUD–比MBProgressHUD更好用的 iOS进度提示组件

简介 ? SVProgressHUD是简单易用的显示器,用于指示一个持续进行的任务的进度. 最新示例: 点击下载 快速入门 安装 通过Cocoapods pod...

3668
来自专栏君赏技术博客

百思不得姐数据挖掘第三篇

播放视频的界面现在只剩下视频的功能了,对于这种播放视频的应该属于功能块。我们可以单独把这个功能提取出来。

1282
来自专栏王磊的博客

IE与IE内核浏览器的那点事

真正的IE浏览器与IE内核的浏览器是不是一回事呢,是不是完全一样咧?如果不一样又有什么关系咧?下面一起来找答案吧! 知识补给:   1.浏览内核 简介   2....

3536
来自专栏pangguoming

c#以POST方式模拟提交表单

这是我一年前写的一个用C#模拟以POST方式提交表单的代码,现在记录在下面,以免忘记咯。那时候刚学C#~忽忽。。很生疏。。代码看上去也很幼稚 臃肿不堪 #reg...

3239

扫码关注云+社区

领取腾讯云代金券