android 中View SurfaceView SurfaceTexture 的区别

Android SurfaceView 与 View 的区别:

http://bbs.51cto.com/thread-965972-1.html
     如果你的游戏不吃CPU,用View就比较好,符合标准Android操作方式,由系统决定刷新surface的时机。
  但如果很不幸的,你做不到不让你的程序吃CPU,你就只好使用SurfaceView来强制刷新surface了,不然系统的UI进程很可能抢不过你那些吃CPU的线程。
  当然其实不止这两种方法来刷新Surface的,这两种只是纯Java应用比较常见的方法。
  SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。
  那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。
  当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中 thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。
  所以基于以上,根据游戏特点,一般分成两类。
  1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
  2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。
  一般2D游戏开发使用SurfaceView足够,因为它也是google专们扩展用于2D游戏开发的画布
  使用普通的游戏画布(Android中2D专用游戏画布)中进行绘制图片,然后在GLSurfaceView(Android中3D游戏专用画布)中渲染图片的对比中发现GLSurfaceView的效率高于SurfaceView的30倍;GLSurfaceView的效率主要是因为机器硬件的GPU加速,现在flash技术也有了GPU加速技术;
  下面总结一下:
  一般2D游戏使用SurfaceView足够,所以不要认为什么都要使用GLSurfaceView(openGL),而且   GLSurfaceView的弊端在于适配能力差,因为很多机型中是没有GPU加速的。【这里总结的很好】

SurfaceTexture 与 SurfaceView 的区别:

http://www.cnblogs.com/hrlnw/p/3277300.html
     SurfaceTexture是从Android3.0(API 11)加入的一个新类。这个类跟SurfaceView很像,可以从camera preview或者video decode里面获取图像流(image stream)。但是,和SurfaceView不同的是,SurfaceTexture在接收图像流之后,不需要显示出来。有做过Android camera开发的人都知道,比较头疼的一个问题就是,从camera读取到的预览(preview)图像流一定要输出到一个可见的(Visible)SurfaceView上,然后通过Camera.PreviewCallback的public void onPreviewFrame(byte[] data, Camera camera)函数来获得图像帧数据的拷贝。这就存在一个问题,比如我希望隐藏摄像头的预览图像或者对每一帧进行一些处理再显示到手机显示屏上,那么在Android3.0之前是没有办法做到的,或者说你需要用一些小技巧,比如用其他控件把SurfaceView给挡住,注意这个显示原始camera图像流的SurfaceView其实是依然存在的,也就是说被挡住的SurfaceView依然在接收从camera传过来的图像,而且一直按照一定帧率去刷新,这是消耗cpu的,而且如果一些参数设置的不恰当,后面隐藏的SurfaceView有可能会露出来,因此这些小技巧并不是好办法。但是,有了SurfaceTexture之后,就好办多了,因为SurfaceTexture不需要显示到屏幕上,因此我们可以用SurfaceTexture接收来自camera的图像流,然后从SurfaceTexture中取得图像帧的拷贝进行处理,处理完毕后再送给另一个SurfaceView用于显示即可。

     在该应用场景中,我需要从camera读取图像流,然后对其处理,最后将处理结果显示到手机屏幕上。因此我写了一个GameDisplay类用于处理以上所有事务,而在MainActivity中,只需要创建一个GameDisplay类的实例并且初始化即可。
package com.song.camgame;

import java.io.IOException;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class GameDisplay extends SurfaceView implements SurfaceHolder.Callback,
Camera.PreviewCallback{
    public final static String TAG="GameDisplay";
    
    private static final int MAGIC_TEXTURE_ID = 10;
    public static final int DEFAULT_WIDTH=800;
    public static final int DEFAULT_HEIGHT=480;
    public static final int BLUR = 0;
    public static final int CLEAR = BLUR + 1;
    //public static final int PAUSE = PLAY + 1;
    //public static final int EXIT = PAUSE + 1;
    public SurfaceHolder gHolder;
    public  SurfaceTexture gSurfaceTexture;
    public Camera gCamera;
    public byte gBuffer[];
    public int textureBuffer[];
    public ProcessThread gProcessThread;
    private int bufferSize;
    private Camera.Parameters parameters;
    public int previewWidth, previewHeight;
    public int screenWidth, screenHeight;
    public Bitmap gBitmap;
    private Rect gRect;
    // timer
    private Timer sampleTimer;
    private TimerTask sampleTask;
    
    public GameDisplay(Context context,int screenWidth,int screenHeight) {
        super(context);
        gHolder=this.getHolder();
        gHolder.addCallback(this);
        gHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        gSurfaceTexture=new SurfaceTexture(MAGIC_TEXTURE_ID);
        this.screenWidth=screenWidth;
        this.screenHeight=screenHeight;
        gRect=new Rect(0,0,screenWidth,screenHeight);
        Log.v(TAG, "GameDisplay initialization completed");
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        Log.v(TAG, "GameDisplay surfaceChanged");
        parameters = gCamera.getParameters();    
        List<Size> preSize = parameters.getSupportedPreviewSizes();
        previewWidth = preSize.get(0).width;
        previewHeight = preSize.get(0).height;
        for (int i = 1; i < preSize.size(); i++) {
            double similarity = Math
                    .abs(((double) preSize.get(i).height / screenHeight)
                            - ((double) preSize.get(i).width / screenWidth));
            if (similarity < Math.abs(((double) previewHeight / screenHeight)
                                    - ((double) previewWidth / screenWidth))) {
                previewWidth = preSize.get(i).width;
                previewHeight = preSize.get(i).height;
            }
        }
        gBitmap= Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
        parameters.setPreviewSize(previewWidth, previewHeight);
        gCamera.setParameters(parameters);
        bufferSize = previewWidth * previewHeight;
        textureBuffer=new int[bufferSize];
        bufferSize  = bufferSize * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat()) / 8;
        gBuffer = new byte[bufferSize];
        gCamera.addCallbackBuffer(gBuffer);
        gCamera.setPreviewCallbackWithBuffer(this);
        gCamera.startPreview();
        //gProcessThread = new ProcessThread(surfaceView,handler,null,previewWidth,previewHeight);
        //processThread.start();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.v(TAG, "GameDisplay surfaceCreated");
        if (gCamera == null) {
            gCamera = Camera.open();
        }
        gCamera.setPreviewTexture(gSurfaceTexture);
        //sampleStart();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.v(TAG, "GameDisplay surfaceDestroyed");
        //gProcessThread.isRunning=false;
        //sampleTimer.cancel();
        //sampleTimer = null;
        //sampleTask.cancel();
        //sampleTask = null;
        gCamera.stopPreview();
        gCamera.release();
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        Log.v(TAG, "GameDisplay onPreviewFrame");
        //gProcessThread.raw_data=data;    
        camera.addCallbackBuffer(gBuffer);
        for(int i=0;i<textureBuffer.length;i++)
            textureBuffer[i]=0xff000000|data[i];
        gBitmap.setPixels(textureBuffer, 0, previewWidth, 0, 0, previewWidth, previewHeight);
        synchronized (gHolder)
        {        
            Canvas canvas = this.getHolder().lockCanvas();
            canvas.drawBitmap(gBitmap, null,gRect, null);
            //canvas.drawBitmap(textureBuffer, 0, screenWidth, 0, 0, screenWidth, screenHeight, false, null);
            this.getHolder().unlockCanvasAndPost(canvas);
        }
        
    }
    
    public void sampleStart() {
        Log.v(TAG, "GameDisplay sampleStart");
        sampleTimer = new Timer(false);
        sampleTask = new TimerTask() {
            @Override
            public void run() {
                gProcessThread.timer=true;
            }
        };
        sampleTimer.schedule(sampleTask,0, 80);
    }
}
以上程序中115-130行是最重要的部分,data是从SurfaceTexture获得的camera图像帧的拷贝,119-121行对其进行了简单的处理,122-128行将处理过的图像数据传递给负责显示的SurfaceView并显示出来。
MainActivity对GameDisplay的调用如下:
//声明
private GameDisplay gameDisplay;
//初始化
gameDisplay.setVisibility(SurfaceView.VISIBLE);
DisplayMetrics dm = getResources().getDisplayMetrics();
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;
gameDisplay= new GameDisplay(this,dm.widthPixels,dm.heightPixels);
//加入到当前activity的layout中
FrameLayout root = (FrameLayout) findViewById(R.id.root);
root.addView(gameDisplay,0);

补充:

SurfaceView 与 SurfaceTexture 的区别还多了一条,SurfaceTexture 比 SurfaceView 更耗电。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值