SurfaceView 和 GLSurfaceView

Android 應用程式架構 UI 是以物件階層為基礎,從 View 開始。 所有 UI 元素都會經過一系列測量和版面配置程序,以便放入矩形區域。接著,當應用程式移至前景時,架構會將所有可見的檢視區塊物件,算繪至 WindowManager 設定的介面。應用程式的 UI 執行緒會執行版面配置和算繪作業,將每個影格算繪至緩衝區。

SurfaceView

SurfaceView 是一種元件,可用於在檢視區塊階層中嵌入額外的複合層。SurfaceView 會採用與其他檢視區塊相同的版面配置參數,因此可以像其他檢視區塊一樣操作,但 SurfaceView 的內容是透明的。

使用外部緩衝區來源 (例如 GL 環境或媒體解碼器) 進行算繪時,您需要將緩衝區從緩衝區來源複製到螢幕上,才能顯示緩衝區。使用 SurfaceView 即可達成此目的。

當 SurfaceView 的檢視區塊元件即將顯示時,架構會要求 SurfaceControl 向 SurfaceFlinger 要求新的介面。如要在建立或終止 Surface 時接收回呼,請使用 SurfaceHolder 介面。根據預設,架構會將新建立的介面放在應用程式 UI 介面後方。您可以覆寫預設的 Z 順序,將新介面放在最上層。

如果您需要轉譯至獨立的 Surface,例如使用 Camera API 或 OpenGL ES 環境轉譯,則使用 SurfaceView 轉譯會很有幫助。使用 SurfaceView 算繪時,SurfaceFlinger 會直接將緩衝區組合到螢幕上。如果沒有 SurfaceView,您需要將緩衝區合成至螢幕外表面,然後合成至螢幕,因此使用 SurfaceView 進行算繪可省下額外工作。使用 SurfaceView 算繪後,請使用 UI 執行緒與活動生命週期協調,並視需要調整檢視區塊的大小或位置。接著,硬體 Compositor 會混合應用程式 UI 和其他圖層。

新介面是 BufferQueue 的生產者端,而消費者是 SurfaceFlinger 層。您可以使用任何可饋送 BufferQueue 的機制更新介面,例如介面提供的 Canvas 函式、附加 EGLSurface 並使用 GLES 在介面上繪圖,或設定媒體解碼器來寫入介面。

SurfaceView 和活動生命週期

使用 SurfaceView 時,請從主 UI 執行緒以外的執行緒算繪 Surface。

如果活動含有 SurfaceView,則有兩個獨立但相互依存的狀態機器:

  • 應用程式 onCreate/onResume/onPause
  • 建立/變更/刪除 Surface

活動啟動時,您會依序收到下列回呼:

  1. onCreate()
  2. onResume()
  3. surfaceCreated()
  4. surfaceChanged()

如果按一下返回,會看到:

  1. onPause()
  2. surfaceDestroyed() (在介面消失前呼叫)

如果旋轉螢幕,系統會拆除並重新建立活動,您也會取得完整週期。如要確認是否為快速重新啟動,請檢查 isFinishing()。活動的開始/停止速度可能很快,導致 surfaceCreated() 發生在 onPause() 之後。

如果輕觸電源鍵關閉螢幕,只會看到 onPause(),不會看到 surfaceDestroyed()。表面仍處於啟用狀態,因此可以繼續算繪。如果繼續要求,您仍可取得 Choreographer 事件。如果螢幕鎖定會強制使用其他螢幕方向,當裝置取消空白時,活動可能會重新啟動。否則,您可以使用與先前相同的介面退出螢幕空白狀態。

視螢幕空白時要執行的動作而定,執行緒的生命週期可以繫結至介面或活動。執行緒可以在活動啟動/停止時,或在介面建立/毀損時啟動/停止。

在活動啟動/停止時啟動/停止執行緒,可與應用程式生命週期完美搭配。您會在 onResume() 中啟動算繪器執行緒,並在 onStop() 中停止執行緒。建立及設定執行緒時,有時介面已存在,有時則否 (例如,使用電源鍵切換螢幕後,介面仍處於啟用狀態)。您必須等待表面建立完成,才能在執行緒中初始化。您無法在 surfaceCreate() 回呼中初始化,因為如果未重新建立介面,回呼就不會再次觸發。請改為查詢或快取介面狀態,然後轉送至算繪器執行緒。

在建立/終止介面時啟動/停止執行緒是個好方法,因為介面和算繪器在邏輯上是相互交織的。您會在建立介面後啟動執行緒,避免一些執行緒間的通訊問題,並轉送建立/變更介面的訊息。如要確認螢幕空白時停止算繪,螢幕恢復時繼續算繪,請告知 Choreographer 停止叫用影格繪製回呼。onResume() 如果轉譯器執行緒正在執行,則會繼續回呼。不過,如果根據影格間經過的時間製作動畫,下一個事件抵達前可能會出現很大的間隔;使用明確的暫停/繼續訊息可以解決這個問題。

無論執行緒的生命週期與活動或介面相關,這兩個選項的重點都是如何設定算繪器執行緒,以及是否正在執行。另一個相關問題是,當活動終止 (在 onStop()onSaveInstanceState() 中) 時,如何從執行緒中擷取狀態;在這種情況下,將執行緒的生命週期繫結至活動是最佳做法,因為在加入算繪器執行緒後,即可存取算繪執行緒的狀態,不必使用同步基本體。

GLSurfaceView

GLSurfaceView 類別提供輔助類別,用於管理 EGL 情境、執行緒間通訊,以及與活動生命週期互動。您不需要使用 GLSurfaceView 即可使用 GLES。

舉例來說,GLSurfaceView 會建立用於算繪的執行緒,並在其中設定 EGL 情境。活動暫停時,系統會自動清除狀態。大多數應用程式不需要瞭解 EGL,就能搭配 GLSurfaceView 使用 GLES。

在大多數情況下,GLSurfaceView 可簡化 GLES 的使用方式。在某些情況下,這項功能可能會造成干擾。