`
wo_deqing
  • 浏览: 64192 次
文章分类
社区版块
存档分类
最新评论

Android之SurfaceView简介(一)

 
阅读更多

. SurfaceView介绍

通常情况程序的View和用户响应都是在同一个线程中处理的,这也是为什么处理长时间事件(例如访问网络)需要放到另外的线程中去(防止阻塞当前UI线程的操作和绘制)。但是在其他线程中却不能修改UI元素,例如用后台线程更新自定义View(调用View的在自定义View中的onDraw函数)是不允许的。
<wbr><wbr><br> 如果需要在另外的线程绘制界面、需要迅速的更新界面,或者渲染UI界面需要较长的时间,这种情况就要使用SurfaceView了。SurfaceView中包含一个Surface对象,而Surface是可以在后台线程中绘制的。</wbr></wbr>
SurfaceView的性质决定了其比较适合一些场景:需要界面迅速更新、对帧率要求较高的情况。
使用SurfaceView需要注意以下几点情况:
(1)SurfaceView和SurfaceHolder.Callback函数都从当前SurfaceView窗口线程中调用(一般而言就是程序的主线程)。
(2)有关资源状态要注意和绘制线程之间的同步。
(3)<wbr><wbr>在绘制线程中必须先合法的获取Surface才能开始绘制内容,在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之间的状态为合法的,在Surface类型为SURFACE_TYPE_PUSH_BUFFERS时是不合法的。</wbr></wbr>
(4)<wbr><wbr>额外的绘制线程会消耗系统的资源,在使用SurfaceView的时候要注意这点。</wbr></wbr><wbr><wbr><br><span style="font-size:18px"></span><wbr><wbr><br></wbr></wbr></wbr></wbr>

2. 使用SurfaceView

只要继承SurfaceView类并实现SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView了,SurfaceHolder.Callback在底层的Surface状态发生变化的时候通知View,SurfaceHolder.Callback具有如下的接口:
surfaceCreated(SurfaceHolder holder):当Surface第一次创建后会立即调用该函数。程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface。
surfaceChanged(SurfaceHolder holder, int format, int width,int height):当Surface的状态(大小和格式)发生变化的时候会调用该函数,在surfaceCreated调用后该函数至少会被调用一次。
surfaceDestroyed(SurfaceHolder holder):当Surface被摧毁前会调用该函数,该函数被调用后就不能继续使用Surface了,一般在该函数中来清理使用的资源。<wbr><wbr><br><wbr><wbr><br> 通过SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface 就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas.lockCanvas()或则Canvas.lockCanvas(Rect dirty)函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑,或者尚未创建调用该函数会返回null,在 unlockCanvas() 和 lockCanvas()中Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect dirty)函数来指定一个dirty区域,这样该区域外的内容会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在Surface绘制过程中不会被改变(被摧毁、修改)。</wbr></wbr></wbr></wbr>
当在Canvas中绘制完成后,调用函数unlockCanvasAndPost(Canvas canvas)来通知系统Surface已经绘制完成,这样系统会把绘制完的内容显示出来。为了充分利用不同平台的资源,发挥平台的最优效果可以通过SurfaceHolder的setType函数来设置绘制的类型,目前接收如下的参数:
SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface<wbr><wbr><br><span style="white-space:pre"></span> SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface<wbr><wbr><br><span style="white-space:pre"></span> SURFACE_TYPE_GPU:适用于GPU加速的Surface<wbr><wbr><br><span style="white-space:pre"></span> SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。</wbr></wbr></wbr></wbr></wbr></wbr>

注意:一个SurfaceView只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed()调用之间是可用的,其他时间是得不到它的Canvas对象的(null)。


3. SurfaceView和View的区别

SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。

在UI的主线程中更新画面可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。当使用SurfaceView时,由于是在新的线程中更新画面所以不会阻塞UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要SurfaceView中的Thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。

所以基于以上,根据游戏特点,一般分成两类:
(1)被动更新画面的。比如棋类,用View就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为在这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
(2)主动更新画面的。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞UI主线程。所以显然view不合适,需要surfaceView来控制。


我们在屏幕上看到的这些view,在屏幕上看到的就是画面,在内存中就是一块内存区。绘图的时候,就是显示的硬件如显卡将内存区的这块图形数据绘制到屏幕上。所以,从内存的角度去看这些东西,会比较好理解。

surface是surfaceview中的一个可见部分。我们知道,我们看到的屏幕上的图形,是二维的,我们看到的就是长和宽,其实,在内部实际上是三维的,另一个维度,就是层layer。我们用visio绘图,都会看到这种情况,一个图形会将另个图形遮住,是因为这个图形在上层。如果有同AutoCAD的经验,对这个更容易理解。我们看到的图形实际上是很多图形一层层的叠加在一起的,这些图形元素完全不可见,或者部分可见部分不可见,或者完全可见。

这样看来,surface就可以这样理解:它是内存中一块区域,它是surfaceview可见那个部分,绘图操作作用于它,然后它就会被显卡之类的显示控制器绘制到屏幕上。

surface是个啥,大概已经有了些概念了。因为它对应了一个内存区,大家都知道,内存区的对象是有生命周期的,可以动态的申请创建和销毁,当然也可能会更新。于是,就有了作用于这个内存区的操作,这些操作就是surfaceCreated/Changed/Destroyed。三个操作放在一起,就是callback,所以在很多例子里看到,会有callback。

callback,是回调,意思是自己能干一些活,不过自己不去主动做,而是到别人那里去登记一下,别人需要的时候,就会叫我去做。就这个例子而言,可以打这个比方,某建筑工人队伍A,能盖房子,能装修,也能拆房子。可是他自己不去主动的做这些事情,而是去向开发商B去登记这三种能力,当然了,他把这三种能力打了包,叫做A的能力。啥时候,B有活干了,就叫A去做,或者是盖房子,或者是装修,或者是拆建筑。在这个例子中,能力包就是callback.

说了这个例子,其实就解释了 surface相关的一些东西,callback已经说过了,下面来说说其他的。假设surface就是一栋房子,那么surface拥有surfaceHolder,谁呢?在这个例子中好比建筑队A。盖房子对应的就是surfaceCreated, 拆房子就对应了surfaceDestroyed,装修就对应了surfaceChanged.

surface有生存期,好比房子有生存期,在建造以后就存在,在拆了之后就没有了。装修必须发生在这之间。同样的,surface的change必须发生在created和destroyed之间。

surfaceview知道surface的holder是谁,在surfaceview生成的时候,会调用getHolder得到holder,然后holder会调用addCallback将三个callback函数注册。

holder拥有对于surface的控制权。在很多程序中,会在surfaceCreated的函数实现中创建另一个线程。所以在这里有两个线程,一个是UI线程,另一个负责画图的线程。画图线程由UI线程调用surfaceCreated的时候创建,在surfaceDestroyed调用的时候放回到线程池。在这中间,画图线程负责图形的绘制。

在这种模型下,UI线程和画图线程各司其职,前者主要负责和用户的交互,而后者,在负责绘制图形。这样,绘制图形的时候如果时间较长,不会阻塞用户的输入。

我们知道,线程共享内存数据,所以, surface对于两个线程是共享的。所以,为了避免在画图的时候,UI线程也对surface进行操作,在画图前,需要对surface加锁。这个工作是有holder干的,holder会先锁住surface中的一块holder.lockCanvas,我们叫canvas,然后,在上面绘画,画完之后,会解锁unlockCanvasAndPost。

在应用中,画图可以是一次性的,也可以是由定时器触发的定时的画。实现的都是runnable类中的run方法。关于runnable类,这个Java中定义的,并不是android独有的,可以参考Java的referrence.

这个模型最大的好处就是,画图不依赖于UI线程,不会阻塞UI线程。而单纯的view是依赖于UI线程画图的。对于完全依赖于用户的输入进行图像显示的更新的,用view是可以的,但是如果能够自动的进行绘图,而不需等待用户的输入,surfaceview无疑是更好的选择。


有图有真相,请看图\" src=}


分享到:
评论

相关推荐

    Android中SurfaceView截屏

    在Android开发中,`SurfaceView`是一个非常重要的视图组件,尤其在处理视频播放、游戏渲染等高性能图形操作时,它的优势在于拥有独立于主线程的渲染机制,能避免阻塞UI更新。然而,由于其特殊的性质,直接通过常规...

    android之surfaceview学习示例

    在Android开发中,SurfaceView是一个非常重要的视图组件,它为开发者提供了在应用程序中实现高性能图形渲染的能力。SurfaceView的设计初衷是为了处理那些需要频繁更新且对性能要求较高的场景,如视频播放、游戏画面...

    Android 通过SurfaceView 实现实时显示摄像头视频

    SurfaceView是Android系统提供的一种用于高效显示动态图像的视图组件,它特别适合处理需要低延迟、高性能的视频流。 首先,了解SurfaceView的基本原理。SurfaceView包含一个Surface对象,该对象存在于应用程序和...

    android之surfaceview游戏开发

    在Android平台上进行游戏开发,SurfaceView是一个至关重要的组件,它为高效、低延迟的图形绘制提供了可能,尤其适合用于创建动态、高性能的游戏画面。本文将深入探讨如何利用SurfaceView进行游戏开发,以及与之相关...

    Android SurfaceView 实现图片缩放 滑动

    在Android开发中,SurfaceView是一种特殊的视图,它允许开发者创建高性能的图形界面,尤其适合处理视频播放或游戏等需要连续刷新的场景。本话题主要探讨如何利用SurfaceView实现图片的缩放和滑动功能。 首先,理解...

    Android圆形相机预览窗口,圆形SurfaceView

    SurfaceView是Android系统提供的一种用于显示多媒体数据(如视频或相机预览)的视图组件。它拥有自己的Surface,可以直接与硬件进行交互,从而提供流畅的动画和低延迟的视频播放。SurfaceView通常用于处理高性能的...

    android surfaceview 悬浮窗圆角 windowmanager

    在Android开发中,`SurfaceView`是一个特殊类型的`View`,它允许开发者在应用程序中创建一个独立于应用程序主线程的渲染表面。`SurfaceView`通常用于处理高性能的图形或者视频播放,因为它可以在单独的线程中进行...

    Android双SurfaceView底部拍照,顶部绘图

    Android双SurfaceView底部拍照,顶部绘图,当SurfaceHolder对象的类型设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS时就只能拍照不能绘制了。为了既能通过SurfaceView拍照又能在上面绘制图形,可以通过双...

    android-SurfaceView 测试Demo

    在Android平台上,SurfaceView是一种特殊的View,用于处理高性能、低延迟的图形绘制,尤其是在开发游戏或者视频播放等需要高效刷新率的应用时。本测试Demo旨在展示如何有效地利用SurfaceView进行游戏开发。以下是对...

    Android SurfaceView 实现实时显示摄像头视频

    `SurfaceView`是Android提供的一种用于高效显示多媒体数据的视图组件,尤其适合处理像视频流这样的实时数据。本篇文章将深入探讨如何利用`SurfaceView`实现实时显示摄像头视频。 首先,我们需要了解`SurfaceView`的...

    Android中SurfaceView的使用

    在Android开发中,SurfaceView是一个非常重要的视图组件,它为开发者提供了在应用程序中实现高性能图形绘制的能力,常用于视频播放、游戏开发等场景。SurfaceView的特性使其能够在单独的线程中进行渲染,从而避免了...

    android surfaceView 双缓冲

    android使用双缓冲辨析及surfaceview使用例子

    安卓Android源码——SurfaceView添加组件view不被组件覆盖.zip

    `SurfaceView` 创建了一个独立的窗口,这个窗口位于应用程序窗口之上,形成了一个“洞”,使得底层的窗口内容得以显示。通常,`SurfaceView` 用于显示连续更新的内容,而上面的组件则用来提供交互功能。由于 `...

    Android下使用SurfaceView播放视频文件

    在Android平台上,SurfaceView是一种特殊的视图,常用于处理高性能的图形渲染,比如播放视频或者游戏画面。在视频播放场景中,SurfaceView提供了一个高效且低延迟的显示机制,能够将视频帧直接绘制到Surface上,减少...

    Android_surfaceView与layout屏幕适配总结

    首先,`SurfaceView`是Android提供的一种特殊视图,它允许开发者在应用程序中直接进行硬件加速的图形绘制,如视频播放、游戏画面等高性能需求的场景。`SurfaceView`有自己的绘图表面,独立于应用程序的主线程,这...

    Android SurfaceView添加组件view不被组件覆盖.zip

    在Android开发中,SurfaceView是一种特殊的视图,它允许开发者在应用程序中创建一个独立的、高性能的绘图表面。SurfaceView通常用于视频播放、游戏或者需要频繁更新图像的场景。然而,有时候当我们尝试在SurfaceView...

    Android-surfaceview-camera

    在Android平台上,SurfaceView是一种特殊的视图,常用于处理复杂的图形和视频流,例如实现摄像头功能。本项目“Android-surfaceview-camera”专注于利用SurfaceView来实现一个自定义的相机应用。下面将详细介绍...

    android surfaceview自定义拍照 绘制头像轮廓

    在Android开发中,`SurfaceView`是一个非常重要的组件,它提供了与硬件图形渲染直接交互的能力。这个项目"android surfaceview自定义拍照 绘制头像轮廓"是基于网上现有的示例代码进行了修改,用于实现一个自定义的...

    Android切换至SurfaceView时闪屏(黑屏闪一下)以及黑屏移动问题的解决方法

    1.最近的项目中,有一个Activity用到Fragment+ViewPager,其中一个fragment中实现了视频播放的功能,包含有SurfaceView。结果,每次打开程序第一次进入到该Activity时都会闪屏黑一下。原因就出在SurfaceView。 详解:...

    Android源码——SurfaceView添加组件view不被组件覆盖.zip

    当一个View被添加到SurfaceView之上时,如果不做特殊处理,通常会被SurfaceView完全遮挡。这是因为SurfaceView的层级更高,它位于一个单独的窗口中,其Z轴位置高于普通的View。 为了解决这个覆盖问题,我们可以采取...

Global site tag (gtag.js) - Google Analytics