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 更耗電。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章