Android SurfaceView

来源:互联网 发布:手机特效魔法软件 编辑:程序博客网 时间:2024/05/19 03:23
一、什么是Surface

           简单的说Surface对应了一块屏幕缓冲区,每个window对应一个Surface,任何View都要画在Surface的Canvas上(后面有原因解释)。传统的view共享一块屏幕缓冲区,所有的绘制必须在UI线程中进行。

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

        1、通过Surface(因为Surface是句柄)就可以获得原生缓冲器以及其中的内容。就像在C++语言中,可以通过一个文件的句柄,就可以获得文件的内容一样。

        2、 原始缓冲区(a raw buffer)是用于保存当前窗口的像素数据的。

        引伸地,可以认为Android中的Surface就是一个用来画图形(graphics)或图像(image)的地方。

        根据Java方面的常规知识,我们知道通常画图是在一个Canvas对象上面进行的,由此,可以推知一个Surface对象中应该包含有一个Canvas(画布)对象。因此,在前面提及的两个意思的基础上,可以再加上一条:

        3、Surface中有一个Canvas成员,专门用于画图的。

        由以上的概括,我们总结如下:Surface中的Canvas成员,是专门用于供程序员画图的场所,就像黑板一样;其中的原始缓冲区是用来保存数据的地方;Surface本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原始缓冲区以及其它方面的内容。

      Surface是用来管理数据的。(句柄)

二、什么是SurfaceView

         说SurfaceView是一个View也许不够严谨,然而从定义中pubilc classSurfaceView extends View{.....}显示SurfaceView确实是派生自View,但是SurfaceView却有自己的Surface,每个SurfaceView创建的时候都会创建一个MyWindow,new MyWindow(this)中的this正是SurfaceView自身,因此将SurfaceView和window绑定在一起,由第一部分我们知道,一个window对应一个Surface,因此SurfaceView也就内嵌了一个自己的Surface,可以认为SurfaceView是用来控制Surface中View的位置和尺寸的。

         SurfaceView就是展示Surface中数据的地方,同时可以认为SurfaceView是用来控制Surface中View的位置和尺寸的。

        大家都知道,传统View及其派生类的更新只能在UI线程,然而UI线程还同时处理其他交互逻辑,这就无法保证View更新的速度和帧率了,而SurfaceView可以用独立的线程进行绘制,因此可以提供更高的帧率,例如游戏,摄像头取景等场景就比较适合SurfaceView来实现。

三、什么是SurfaceHolder

        SurfaceHolder是一个接口,其作用就像一个关于Surface的监听器,提供访问和控制SurfaceView内嵌的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(Rect  dirty):同上。但只锁定dirty所指定的矩形区域,因此效率更高。

       4、abstract void unlockCanvasAndPost(Canvas  canvas):当修改Surface中的数据完成后,释放同步锁,并提交改变,然后将新的数据进行展示,同时Surface中相关数据会被丢失。

        2、3、4中的同步锁机制的目的,就是为了在绘制的过程中,Surface中的数据不会被改变。lockCanvas是为了防止同一时刻多个线程对同一canvas写入。

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

四、什么是SurfaceHolder.Callback

        SurfaceHolder.Callback主要是当底层的Surface被创建、销毁或者改变时提供回调通知,由于绘制必须在Surface被创建后才能进行,因此SurfaceHolder.Callback中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。


五、Surface的缓冲

SurfaceView包含Surface(含Canvas)、SurfaceView内部又有自己的Canvas,两个Canvas的存在实现画面缓冲和显示


六、SurfaceView和View的本质区别

surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。
在一般的情况下,应用程序的View都是在相同的GUI线程中绘制的。这个主应用程序线程同时也用来处理所有的用户交互(例如,按钮单击或者文本输入)。
把容易阻塞的处理移动到后台线程中。遗憾的是,对于一个View的onDraw方法,不能这样做,因为从后台线程修改一个GUI元素会被显式地禁止的。
当需要快速地更新View的UI,或者当渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。 SurfaceView封装了一个Surface对象,而不是Canvas。这一点很重要,因为Surface可以使用后台线程绘制。对于那些资源敏感的 操作,或者那些要求快速更新或者高速帧率的地方,例如,使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。独立于GUI线程进行绘图的代价是额外的内存消耗,所以,虽然它是创建定制的View的有效方式--有时甚至是必须的,但是使用Surface View的时候仍然要保持谨慎。


main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent" android:layout_height="fill_parent"android:orientation="vertical"><LinearLayout android:id="@+id/LinearLayout01"android:layout_width="wrap_content" android:layout_height="wrap_content"><Button android:id="@+id/Button01" android:layout_width="wrap_content"android:layout_height="wrap_content" android:text="单个独立线程"></Button><Button android:id="@+id/Button02" android:layout_width="wrap_content"android:layout_height="wrap_content" android:text="两个独立线程"></Button></LinearLayout><SurfaceView android:id="@+id/SurfaceView01"android:layout_width="fill_parent" android:layout_height="fill_parent"></SurfaceView></LinearLayout>

package com.testSurfaceView;import java.lang.reflect.Field;import java.util.ArrayList;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.os.Bundle;import android.util.Log;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.widget.Button;public class testSurfaceView extends Activity {/** Called when the activity is first created. */Button btnSingleThread, btnDoubleThread;SurfaceView sfv;SurfaceHolder sfh;ArrayList<Integer> imgList = new ArrayList<Integer>();int imgWidth, imgHeight;Bitmap bitmap;//独立线程读取,独立线程绘图@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);btnSingleThread = (Button) this.findViewById(R.id.Button01);btnDoubleThread = (Button) this.findViewById(R.id.Button02);btnSingleThread.setOnClickListener(new ClickEvent());btnDoubleThread.setOnClickListener(new ClickEvent());sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);sfh = sfv.getHolder();sfh.addCallback(new MyCallBack());// 自动运行surfaceCreated以及surfaceChanged}class ClickEvent implements View.OnClickListener {@Overridepublic void onClick(View v) {if (v == btnSingleThread) {new Load_DrawImage(0, 0).start();//开一条线程读取并绘图} else if (v == btnDoubleThread) {new LoadImage().start();//开一条线程读取new DrawImage(imgWidth + 10, 0).start();//开一条线程绘图}}}class MyCallBack implements SurfaceHolder.Callback {@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {Log.i("Surface:", "Change");}@Overridepublic void surfaceCreated(SurfaceHolder holder) {Log.i("Surface:", "Create");// 用反射机制来获取资源中的图片ID和尺寸Field[] fields = R.drawable.class.getDeclaredFields();for (Field field : fields) {if (!"icon".equals(field.getName()))// 除了icon之外的图片{int index = 0;try {index = field.getInt(R.drawable.class);} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 保存图片IDimgList.add(index);}}// 取得图像大小Bitmap bmImg = BitmapFactory.decodeResource(getResources(),imgList.get(0));imgWidth = bmImg.getWidth();imgHeight = bmImg.getHeight();}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {Log.i("Surface:", "Destroy");}}/* * 读取并显示图片的线程 */class Load_DrawImage extends Thread {int x, y;int imgIndex = 0;public Load_DrawImage(int x, int y) {this.x = x;this.y = y;}public void run() {while (true) {Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x+ imgWidth, this.y + imgHeight));Bitmap bmImg = BitmapFactory.decodeResource(getResources(),imgList.get(imgIndex));c.drawBitmap(bmImg, this.x, this.y, new Paint());imgIndex++;if (imgIndex == imgList.size())imgIndex = 0;sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容}}};/* * 只负责绘图的线程 */class DrawImage extends Thread {int x, y;public DrawImage(int x, int y) {this.x = x;this.y = y;}public void run() {while (true) {if (bitmap != null) {//如果图像有效Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x+ imgWidth, this.y + imgHeight));c.drawBitmap(bitmap, this.x, this.y, new Paint());sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容}}}};/* * 只负责读取图片的线程 */class LoadImage extends Thread {int imgIndex = 0;public void run() {while (true) {bitmap = BitmapFactory.decodeResource(getResources(),imgList.get(imgIndex));imgIndex++;if (imgIndex == imgList.size())//如果到尽头则重新读取imgIndex = 0;}}};}


原创粉丝点击