1. 程式人生 > >SurfaceView原始碼分析以及使用

SurfaceView原始碼分析以及使用

概述

SurfaceView是Android中比較特殊的一類檢視,它與普通的View最主要的區別是它和它的檢視容器並不是在一個檢視層。

為什麼要使用SurfaceView

我們知道Android系統中是提供了View來進行繪圖處理,然後可以通過invalidate方法通知系統去呼叫view.onDraw方法去對介面進行重繪,而Android系統是通過發出VSYNC訊號來進行螢幕的重繪,可以看出View是主動重新整理,而且這個重新整理時間是16ms,如果在16ms中沒有執行完需要的操作,使用者就會看著卡頓,如果draw方法中需要處理的邏輯太多,或者是遊戲介面,需要頻繁重新整理複雜的介面,這樣就會阻塞主執行緒,從而造成畫面上的卡頓。而SurfaceView,它的繪製是在另外的執行緒中,不會去阻塞主執行緒,另外它底層實現了雙緩衝機制。

從網上摘錄了一段對雙緩衝機制的介紹

雙緩衝技術是遊戲開發中的一個重要的技術。當一個動畫爭先顯示時,程式又在改變它,前面還沒有顯示完,程式又請求重新繪製,這樣螢幕就會不停地閃爍。而雙緩衝技術是把要處理的圖片在記憶體中處理好之後,再將其顯示在螢幕上。雙緩衝主要是為了解決反覆區域性刷屏帶來的閃爍。把要畫的東西先畫到一個記憶體區域裡,然後整體的一次性畫出來。

SurfaceView中的MVC框架

我們要了解SurfaceView,還必須要了解和它息息相關的其他兩個元件:Surface和SurfaceHolder。Surface其實就檢視資料,SurfaceHolder我們都知道是個介面,用來進行繪製。而SurfaceView是顯示檢視並且和使用者互動的介面。而MVC(Model-View-Controller)框架,model是資料,也就是這裡的Surface,View是用來顯示的,也就是SurfaceView,而控制器,也就是這裡SurfaceHolder。

分析原始碼

  • Surface、SurfaceHolder、surfaceView

Surface

java.lang.Object
↳ android.view.Surface

// Handle onto a raw buffer that is being managed by the screen compositor

public class Surface implements Parcelable {
          //code....
}
  • 首先看一下這個Surface類,實現了Parcelable介面進行了序列化(可以在程序中傳遞該類物件)用來處理螢幕顯示緩衝區的資料。
  • 原始碼中的註釋是:Handle onto a raw buffer that is being managed by the screen compositor 翻譯一下就是 對由螢幕影象合成器管理的原始緩衝區進行處理,也就是用來獲取原始緩衝區以及其中的內容,原始緩衝區(raw buffer)是用來儲存當前視窗的畫素資料,由此可知Surface就是Android用來繪圖的地方,
  • 既然需要繪圖,那肯定需要畫布,也就是在其內部定義了Canvas物件

    private final Canvas mCanvas = new CompatibleCanvas();
    
  • CompatibleCanvas是Surface的內部類,它其中包含了一個Matrix物件,這個矩陣本質就是一塊記憶體區域,對View的各種繪圖操作就儲存在這片記憶體中。
  • 這個內部類的作用是為了相容Android各個解析度的螢幕,根據不同螢幕的解析度處理不同的影象資料。原始碼註釋:A Canvas class that can handle the compatibility mode.
 private final class CompatibleCanvas extends Canvas {
     // A temp matrix to remember what an application obtained via {@link getMatrix}
     private Matrix mOrigMatrix = null;

     @Override
     public void setMatrix(Matrix matrix) {
         if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
             // don't scale the matrix if it's not compatibility mode, or
             // the matrix was obtained from getMatrix.
             super.setMatrix(matrix);
         } else {
             Matrix m = new Matrix(mCompatibleMatrix);
             m.preConcat(matrix);
             super.setMatrix(m);
         }
     }

     @SuppressWarnings("deprecation")
     @Override
     public void getMatrix(Matrix m) {
         super.getMatrix(m);
         if (mOrigMatrix == null) {
             mOrigMatrix = new Matrix();
         }
         mOrigMatrix.set(m);
     }
 }
lockCanvas
  • 這個方法很重要,這個方法其實並不是SurfaceView呼叫的,是由surfaceHolder呼叫。API中的解釋:
  • Gets a Canvas for drawing into this surface. ( 獲取當前正在繪畫的canvas物件 ) After drawing into the provided Canvas, the caller must invoke unlockCanvasAndPost(Canvas) to post the new contents to the surface.(繪製完一幀資料之後,需要呼叫unlockCanvasAndPost釋放畫布,然後把繪製好的內容Post到螢幕上去顯示)
  • 也就是說在繪畫的時候,這個畫布是被鎖定的,也就是說只有當前的繪製操作完成並且畫布解鎖以後才能對畫布進行其他的操作。
unlockCanvasAndPost
  • Posts the new contents of the Canvas to the surface and releases the Canvas. 將新繪製的內容傳遞給Surface,這個canvas就會釋放掉
總結

Surface中提供一個可以用來處理不同螢幕解析度的Canvas,這個Canvas就是用來提供給程式設計師畫畫的,在畫畫的過程當中,需要先鎖定畫布,只有在完成當前繪製,把資料post給Surface並且解鎖畫布之後,,才可以繼續繪製其他的內容,原始緩衝區(raw buffer)就是用來儲存資料的,而Surface的作用就類似於C++當中的控制代碼,得到這個控制代碼,就可以得到其中的Canvas、原始緩衝區以及其中內容。

SurfaceHolder

  • android.view.SurfaceHolder 是一個介面
 public interface SurfaceHolder {
 
 }
  • API解釋:
  • Abstract interface to someone holding a display surface.
  • 針對顯示介面Surface的抽象介面
  • Allows you to control the surface size and format, edit the pixels in the surface, and monitor changes to the surface. This interface is typically available through the SurfaceView class.
  • 允許你去控制這個Surface介面的大小、格式以及在上面繪畫,並且可以監控Surface的變化,這個介面通常可以通過 SurfaceView 這個類獲得(SurfaceView的getHolder()方法)這個的作用就是上面提到的Controller的角色
  • When using this interface from a thread other than the one running its SurfaceView, you will want to carefully read the methods lockCanvas() and Callback.surfaceCreated().
  • 如果使用子執行緒來使用這個介面,你需要非常注意 lockCanvas() and Callback.surfaceCreated()這兩個方法的使用
關鍵介面 Callback
  • 它是SurfaceHolder內部的一個介面,有三個方法
  • public void surfaceCreated(SurfaceHolder holder); Surface第一次被建立時候呼叫,Surface由不可見狀態到可見狀態,從這個可見狀態開始一直到surfaceDestroyed銷燬之前,這段時間Surface物件是可以進行操作的
  • public void surfaceChanged(SurfaceHolder holder, int format, int width, int height); Surface的大小和格式改變的時候會呼叫,比如橫豎屏切換的時候就會被呼叫,這個方法在surfaceCreated之後至少會被呼叫一次
  • public void surfaceDestroyed(SurfaceHolder holder); Surface被銷燬時候呼叫,注意呼叫這個方法之後,不能夠在對Surface進行操作了,否則會報錯
總結

SurfaceHolder是一個介面,提供訪問和控制SurfaceView中內嵌的Surface的相關方法。

SurfaceView

java.lang.Object
↳ android.view.View
↳ android.view.SurfaceView

  • 繼承View ,用來顯示Surface 中的資料
API註釋
  • Provides a dedicated drawing surface embedded inside of a view hierarchy.
  • 在螢幕顯示的檢視層嵌入了一塊影象專門用來顯示Surface
  • the SurfaceView punches a hole in its window to allow its surface to be displayed.
  • 這個SurfaceView在window上挖了一個洞以便於Surface被顯示出來
  • 很奇怪對不對,接下來繼續看註釋
    • The surface is Z ordered so that it is behind the window holding its SurfaceView;
      • 這個引入了Z軸,xzy軸的z軸,大家應該不陌生哈,這裡是什麼意思呢?也就是說SurfaceView所在的檢視層級的Z 軸位置 其實是小於其宿主Activity視窗的layer的Z軸位置,也就是其實是在Activity檢視層的下方的
      • 那為什麼能看到SurfaceView呢???結合上面的挖了個洞,其實通俗的說就是在牆上鑿了洞,洞上呢裝了塊玻璃,這樣你就能看到後面的內容了,這裡的牆其實就是Activity所在的層級
  • The Surface will be created for you while the SurfaceView’s window is visible 這個說明了動畫什麼時候開始,也就是當SurfaceView可見的時候,就可以在Canvas上繪製圖像了,然後把資料傳遞給Surface用來顯示在SurfaceViews上了
  • you should implement SurfaceHolder.
    Callback.surfaceCreated(SurfaceHolder) and SurfaceHolder.Callback.surfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed as the window is shown and hidden.
  • 在使用SurfaceView的地方,需要實現SurfaceHolder.Callback中的回撥,以便於Surface的建立和銷燬以及對其改變的監聽以及響應的處理,其實處理就是Canvas開始繪製圖像時候,需要將資料傳遞給Surface來顯示

例項

如何使用SurfaceView,我寫了個demo,可以下載看下,這裡我就不對demo程式碼進行分析了。demo主要的效果就是手指在螢幕上滑動,顯示出滑動軌跡。

效果圖:

SurfaceViewDemo下載