Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之間的關系

Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之間的關系

編輯:Android開發實例

 一、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)的地方。根據Java方面的常規知識,我們知道通常畫圖是在一個Canvas對象上面進行的,由此,可以推知一個Surface對象中應該包含有一個Canvas對象,事實上的確如此,而且這一點可以很容易通過debug運行程序的方式得到證明(將光標停留在對象變量surface上,會彈出一個對話框,其中紅色方框的內容,就表面surface中有一個CompatileCanvas成員變量)當然,看源代碼也是可以證明這一點:


因此,在前面提及的兩個意思的基礎上,可以再加上一條:

3.      Surface中有一個Canvas成員,專門用於畫圖的。

所以,Surface中的Canvas成員,是專門用於供程序員畫圖的場所,就像黑板一樣;其中的原生緩沖器是用來保存數據的地方;Surface本身的作用類似一個句柄,得到了這個句柄就可以得到其中的Canvas、原生緩沖器以及其它方面的內容。

 

二、SurfaceView

SurfaceView,顧名思義就是Surface的View,通過SurfaceView就可以看到Surface的部分或者全部的內容,下面用一個圖來形象地描述一下Surface和SurfaceView的關系:


也就是說,Surface是用通過SurfaceView才能展示其中的內容。從這個意思上來說,SurfaceView中的View之確切的含義應該是viewport即“視口”的意思,做過數據庫設計的朋友知道,假定一個數據表有20個字段,但我們常常只用到其中的5個字段,那麼就可以在原數據表的基礎上,通過SQL語句CREATEVIEW來創建只包含那5個字段內容的view。

另一方面,SurfaceView是Android中View的子類。事實上,在Android中所有用於界面展示的類皆為View的子類,包括那些不可見的、各種各樣的Layout。

所以說,SurfaceView中的View有兩個含義:

1.      視口(viewport)的意思

2.      SurfaceView是View的派生類

 

在Android中Surface是從Object派生而來,且實現了Parcelable接口。看到Parcelable就讓人能很自然地想到數據容器,SurfaceView就是用來展示Surface中的數據的。在這個層面上而言,Surface就是管理數據的地方,SurfaceView就是展示數據的地方。

 

三、SurfaceHolder

SurfaceHolder是一個接口,其作用就像一個關於Surface的監聽器。提供訪問和控制SurfaceView背後的Surface 相關的方法 (providingaccess and control over this SurfaceView's underlying surface),它通過三個回調方法,讓我們可以感知到Surface的創建、銷毀或者改變。在SurfaceView中有一個方法getHolder,可以很方便地獲得SurfaceView所對應的Surface所對應的SurfaceHolder(有點拗口吧)。

 

除下面將要提到的SurfaceHolder.Callback外,SurfaceHolder還提供了很多重要的方法,其中最重要的就是:

1.        abstract void [url=]addCallback[/url]([url=]SurfaceHolder.Callback[/url]callback)

為SurfaceHolder添加一個SurfaceHolder.Callback回調接口。

2.        abstract [url=]Canvas[/url][url=]lockCanvas[/url]()

獲取一個Canvas對象,並鎖定之。所得到的Canvas對象,其實就是Surface中一個成員。

3.        abstract [url=]Canvas[/url][url=]lockCanvas[/url]([url=]Rect[/url]dirty)

同上。但只鎖定dirty所指定的矩形區域,因此效率更高。

4.        abstract void [url=]unlockCanvasAndPost[/url]([url=]Canvas[/url]canvas)

當修改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 [url=]surfaceChanged[/url]([url=]SurfaceHolder[/url]holder, int format, int width, int height)

當surface發生任何結構性的變化時(格式或者大小),該方法就會被立即調用。

2.      abstract void [url=]surfaceCreated[/url]([url=]SurfaceHolder[/url]holder)

當surface對象創建後,該方法就會被立即調用。

3.      abstract void [url=]surfaceDestroyed[/url]([url=]SurfaceHolder[/url]holder)

當surface對象在將要銷毀前,該方法會被立即調用。

 

在Android SDK文檔中,關於SurfaceView的描述裡面,有一段這樣的話:

One of the purposes of this class is to provide a surface in which a secondarythread can render into the screen. If you are going to use it this way, youneed to be aware of some threading semantics:

 

-        All SurfaceView and SurfaceHolder.Callbackmethods will be called from the thread running the SurfaceView's window(typically the main thread of the application). They thus need to correctlysynchronize with any state that is also touched by the drawing thread.

-      You must ensure that the drawingthread only touches the underlying Surface while it is valid -- betweenSurfaceHolder.Callback.surfaceCreated() andSurfaceHolder.Callback.surfaceDestroyed().

這段話很重要,大致意思如下:

這個類的目的之一,就是提供一個可以用另外一個線程(第二個線程)進行屏幕渲染的surface(譯注:即UI線程和繪制線程可以分離)。如果你打算這樣使用,那麼應當注意一些線程方面的語義:

-           所有SurfaceView和SurfaceHolder.Callback中聲明的方法,必須在運行SurfaceView窗口中的線程中調用(典型地,就是應用的主線程。譯注:即UI線程),因為它們需要正確地將同時被繪制線程訪問的各種狀態進行同步。

-           必須保證,只有在背後的Surface有效的時候 – 在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()這兩個方法調用之間,訪問它。

 

下面,我們通過一個非常簡單例子來實際感受一下(代碼摘自http://www.fengfly.com/plus/view-209419-1.html,並做了一些結構性的改動),請留意代碼中的注釋:


1.        在Eclipse中創建一個Android Project項目TestSurfaceView,並選擇生成缺省的Activity TestSurfaceViewActivity

2.        創建一個繪制線程如下:

 

  1. package com.pat.testsurfaceview; 
  2.  
  3. import android.graphics.Canvas; 
  4. import android.graphics.Color; 
  5. import android.graphics.Paint; 
  6. import android.graphics.Rect; 
  7. importandroid.view.SurfaceHolder; 
  8.  
  9. // 繪制線程 
  10. public class MyThread extendsThread 
  11.          private SurfaceHolder holder; 
  12.          private boolean run; 
  13.          
  14.          publicMyThread(SurfaceHolder holder) 
  15.          { 
  16.                    this.holder = holder; 
  17.                    run = true; 
  18.          } 
  19.  
  20.          @Override 
  21.          public void run() 
  22.          { 
  23.                    int counter = 0; 
  24.                    Canvascanvas = null; 
  25.                    while(run) 
  26.                    { 
  27.                             // 具體繪制工作 
  28.                             try 
  29.                             { 
  30.                                      // 獲取Canvas對象,並鎖定之 
  31.                                      canvas= holder.lockCanvas(); 
  32.                                      
  33.                                      // 設定Canvas對象的背景顏色 
  34.                                      canvas.drawColor(Color.WHITE); 
  35.                                      
  36.                                      // 創建畫筆 
  37.                                      Paintp = new Paint(); 
  38.                                      // 設置畫筆顏色 
  39.                                      p.setColor(Color.BLACK); 
  40.                                      // 設置文字大小 
  41.                                      p.setTextSize(30); 
  42.                                      
  43.                                      // 創建一個Rect對象rect 
  44.                                      Rectrect = new Rect(100, 50, 380, 330); 
  45.                                      // 在canvas上繪制rect 
  46.                                      canvas.drawRect(rect,p); 
  47.                                      // 在canvas上顯示時間 
  48.                                      canvas.drawText("Interval = " + (counter++) + " seconds.", 100, 410, p); 
  49.                                      Thread.sleep(1000); 
  50.                             } 
  51.                             catch(Exception e) 
  52.                             { 
  53.                                      e.printStackTrace(); 
  54.                             } 
  55.                             finally 
  56.                             { 
  57.                                      if(canvas != null) 
  58.                                      { 
  59.                                                // 解除鎖定,並提交修改內容 
  60.                                                holder.unlockCanvasAndPost(canvas); 
  61.                                      } 
  62.                             } 
  63.                    } 
  64.          } 
  65.  
  66.          public boolean isRun() 
  67.          { 
  68.                    return run; 
  69.          } 
  70.          
  71.          public void setRun(boolean run) 
  72.          { 
  73.                    this.run = run; 
  74.          } 

3.      自定義一個SurfaceView類如下:

 

  1. package com.pat.testsurfaceview; 
  2.  
  3.  
  4.  
  5. import android.content.Context; 
  6.  
  7. import android.view.SurfaceHolder; 
  8.  
  9. import android.view.SurfaceView; 
  10.  
  11.  
  12.  
  13. public class MySurfaceView extends SurfaceView 
  14.  
  15. implements 
  16.  
  17. SurfaceHolder.Callback 
  18.  
  19.  
  20.          private SurfaceHolder holder; 
  21.  
  22.          private MyThread myThread; 
  23.  
  24.          
  25.  
  26.          publicMySurfaceView(Context context) 
  27.  
  28.          { 
  29.  
  30.                    super(context); 
  31.  
  32.                    
  33.  
  34.                    // 通過SurfaceView獲得SurfaceHolder對象 
  35.  
  36.                    holder = getHolder(); 
  37.  
  38.                    
  39.  
  40.                    // 為holder添加回調結構SurfaceHolder.Callback 
  41.  
  42.                    holder.addCallback(this); 
  43.  
  44.                    
  45.  
  46.                    // 創建一個繪制線程,將holder對象作為參數傳入,這樣在繪制線程中就可以獲得holder 
  47.  
  48.                    // 對象,進而在繪制線程中可以通過holder對象獲得Canvas對象,並在Canvas上進行繪制 
  49.  
  50.                    myThread = new MyThread(holder); 
  51.  
  52.          } 
  53.  
  54.  
  55.  
  56.          // 實現SurfaceHolder.Callback接口中的三個方法,都是在主線程中調用,而不是在繪制線程中調用的 
  57.  
  58.          @Override 
  59.  
  60.          public voidsurfaceChanged(SurfaceHolder holder, int format, int width, int height) 
  61.  
  62.          { 
  63.  
  64.          } 
  65.  
  66.  
  67.  
  68.          @Override 
  69.  
  70.          public voidsurfaceCreated(SurfaceHolder holder) 
  71.  
  72.          { 
  73.  
  74.                    // 啟動線程。當這個方法調用時,說明Surface已經有效了 
  75.  
  76.                    myThread.setRun(true); 
  77.  
  78.                    myThread.start(); 
  79.  
  80.          } 
  81.  
  82.  
  83.  
  84.          @Override 
  85.  
  86.          public void surfaceDestroyed(SurfaceHolderholder) 
  87.  
  88.          { 
  89.  
  90.                    // 結束線程。當這個方法調用時,說明Surface即將要被銷毀了 
  91.  
  92.                    myThread.setRun(false); 
  93.  
  94.          } 
  95.  

4.    修改TestSurfaceViewActivity.java代碼,使之如下

 

  1. package com.pat.testsurfaceview; 
  2.  
  3. import android.app.Activity; 
  4. import android.os.Bundle; 
  5.  
  6. public class TestSurfaceViewActivity extends Activity 
  7.     @Override 
  8.     public void onCreate(Bundle savedInstanceState) 
  9.     { 
  10.         super.onCreate(savedInstanceState); 
  11.         //setContentView(R.layout.main); 
  12.         setContentView(new MySurfaceView(this)); 
  13.     } 

運行結果: 很顯然,我們可以在MyThread的run方法中,做很多更有意思的事情。弄清楚了Surface、SurfaceView、SurfaceHolder和SurfaceHolder.Callback這些概念,以及它們之間的關系,對我們更好地使用它們應該會有相當大的幫助。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved