一、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)的地方。通常画图是在一个Canvas对象上面进行的,由此,可以推知一 个Surface对象中应该包含有一个Canvas对象,事实上的确如此,而且这一点可以很容易通过debug运行程序的方式得到证明(将光标停留在对象 变量surface上,会弹出一个对话框,其中红色方框的内容,就表面surface中有一个CompatileCanvas成员变量)当然,看源代码也 是可以证明这一点:
所以,Surface中的Canvas成员,是专门用于供程序员画图的场所,就像黑板一样;其中的原生缓冲器是用来保存数据的地方;Surface本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原生缓冲器以及其它方面的内容。
二、SurfaceView
通过SurfaceView就可以看到Surface的部分或者全部的内容:
SurfaceView是Android中View的子类。事实上,在Android中所有用于界面展示的类皆为View的子类,包括那些不可见的、各种各样的Layout。
在Android中Surface是从Object派生而来,且实现了 Parcelable接口。看到Parcelable就让人能很自然地想到数据容器,SurfaceView就是用来展示Surface中的数据的。在这 个层面上而言,Surface就是管理数据的地方,SurfaceView就是展示数据的地方。
三、SurfaceHolder
SurfaceHolder是一个接口,其作用就像一个关于Surface的监听器。 提供访问和控制Surface 相关的方法 ,它通过三个回调方法,让我们可以感知到Surface的创建、销毁或者改变。在SurfaceView中有一个方法 getHolder。
除下面将要提到的SurfaceHolder.Callback外,SurfaceHolder还提供了很多重要的方法,其中最重要的就是:
1. abstract void addCallback(SurfaceHolder.Callback callback)
为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中的数据不会被改变。
从设计模式的高度来看,Surface、SurfaceView和 SurfaceHolder实质上就是广为人知的MVC,即Model-View-Controller。Model就是模型的意思,或者说是数据模型, 或者更简单地说就是数据,也就是这里的Surface;View即视图,代表用户交互界面,也就是这里的 SurfaceView;SurfaceHolder很明显可以理解为MVC中的Controller(控制器)。这样看起来三者之间的关系就清楚了很 多。
四、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对象在将要销毁前,该方法会被立即调用。
在Android SDK文档中,关于SurfaceView的描述里面,有一段这样的话:
这个类的目的之一,就是提供一个可以用另外一个线程(第二个线程)进行屏幕渲染的surface(译注:即UI线程和绘制线程可以分离)。如果你打算这样使用,那么应当注意一些线程方面的语义:
- 所有SurfaceView和 SurfaceHolder.Callback中声明的方法,必须在运行SurfaceView窗口中的线程中调用(典型地,就是应用的主线程。译注:即 UI线程),因为它们需要正确地将同时被绘制线程访问的各种状态进行同步。
- 必须保证,只有在背后的Surface有效的时候 – 在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()这两个方法调用之间,访问它。
下面,我们通过一个非常简单例子来实际感受一下(代码摘自http://www.cnblogs.com/xuling/archive/2011/06/06/android.html,并做了一些结构性的改动),请留意代码中的注释:
1. 在Eclipse中创建一个Android Project项目TestSurfaceView,并选择生成缺省的Activity TestSurfaceViewActivity
2. 创建一个绘制线程如下:
package com.pat.testsurfaceview;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
importandroid.view.SurfaceHolder;
// 绘制线程
public class MyThread extendsThread
{
private SurfaceHolder holder;
private boolean run;
public MyThread(SurfaceHolder holder)
{
this.holder = holder;
run = true;
}
@Override
public void run()
{
int counter = 0;
Canvas canvas = null;
while(run)
{
// 具体绘制工作
try
{
// 获取Canvas对象,并锁定之
canvas= holder.lockCanvas();
// 设定Canvas对象的背景颜色
canvas.drawColor(Color.WHITE);
// 创建画笔
Paintp = new Paint();
// 设置画笔颜色
p.setColor(Color.BLACK);
// 设置文字大小
p.setTextSize(30);
// 创建一个Rect对象rect
Rect rect = new Rect(100, 50, 380, 330);
// 在canvas上绘制rect
canvas.drawRect(rect,p);
// 在canvas上显示时间
canvas.drawText("Interval = " + (counter++) + " seconds.", 100, 410, p);
Thread.sleep(1000);
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(canvas != null)
{
// 解除锁定,并提交修改内容
holder.unlockCanvasAndPost(canvas);
}
}
}
}
public boolean isRun()
{
return run;
}
public void setRun(boolean run)
{
this.run = run;
}
}
3. 自定义一个SurfaceView类如下:
package com.pat.testsurfaceview;
import android.content.Context;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MySurfaceView extends SurfaceView
implements
SurfaceHolder.Callback
{
private SurfaceHolder holder;
private MyThread myThread;
publicMySurfaceView(Context context)
{
super(context);
// 通过SurfaceView获得SurfaceHolder对象
holder = getHolder();
// 为holder添加回调结构SurfaceHolder.Callback
holder.addCallback(this);
// 创建一个绘制线程,将holder对象作为参数传入,这样在绘制线程中就可以获得holder
// 对象,进而在绘制线程中可以通过holder对象获得Canvas对象,并在Canvas上进行绘制
myThread = new MyThread(holder);
}
// 实现SurfaceHolder.Callback接口中的三个方法,都是在主线程中调用,而不是在绘制线程中调用的
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
// 启动线程。当这个方法调用时,说明Surface已经有效了
myThread.setRun(true);
myThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolderholder)
{
// 结束线程。当这个方法调用时,说明Surface即将要被销毁了
myThread.setRun(false);
}
}
4. 修改TestSurfaceViewActivity.java代码,使之如下:
package com.pat.testsurfaceview;
import android.app.Activity;
import android.os.Bundle;
public class TestSurfaceViewActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
setContentView(new MySurfaceView(this));
}
}
运行结果:
很显然,我们可以在MyThread的run方法中,做很多更有意思的事情。弄清楚了 Surface、SurfaceView、SurfaceHolder和SurfaceHolder.Callback这些概念,以及它们之间的关系,对 我们更好地使用它们应该会有相当大的帮助。