SurfaceView 與 GLSurfaceView

Android 應用程式框架 UI 是基於以View開頭的物件層次結構。所有 UI 元素都會經過一系列測量和佈局過程,將它們放入矩形區域。然後,所有可見視圖物件都會渲染到應用程式進入前台時由 WindowManager 設定的表面。應用程式的 UI 執行緒對每個幀的緩衝區執行佈局和渲染。

表面視圖

SurfaceView 是一個元件,可用於在視圖層次結構中嵌入附加的複合層。 SurfaceView 採用與其他視圖相同的佈局參數,因此可以像其他視圖一樣操作它,但 SurfaceView 的內容是透明的。

當您使用外部緩衝區來源(例如 GL 上下文或媒體解碼器)進行渲染時,您需要從緩衝區來源複製緩衝區以在螢幕上顯示緩衝區。使用 SurfaceView 可以讓您做到這一點。

當 SurfaceView 的視圖元件即將變得可見時,框架會要求 SurfaceControl 向 SurfaceFlinger 要求新的 Surface。若要在建立或銷毀表面時接收回呼,請使用SurfaceHolder介面。預設情況下,新建立的表面放置在應用程式 UI 表面的後面。您可以覆寫預設的 Z 順序以將新曲面放在頂部。

當您需要渲染到單獨的表面時,例如使用 Camera API 或 OpenGL ES 上下文進行渲染時,使用 SurfaceView 進行渲染非常有用。當你使用SurfaceView渲染時,SurfaceFlinger直接將緩衝區組合到螢幕上。如果沒有 SurfaceView,您需要將緩衝區合成到螢幕外表面,然後將其合成到螢幕,因此使用 SurfaceView 進行渲染可以消除額外的工作。使用 SurfaceView 渲染後,使用 UI 執行緒與 Activity 生命週期進行協調,並根據需要調整視圖的大小或位置。然後,Hardware Composer 混合應用程式 UI 和其他層。

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

SurfaceView 與 Activity 生命週期

使用 SurfaceView 時,從主 UI 執行緒以外的執行緒渲染表面。

對於具有 SurfaceView 的 Activity,有兩個獨立但相互依賴的狀態機:

  • 應用程式onCreate / onResume / onPause
  • 表面創建/更改/銷毀

當活動開始時,您會依照以下順序收到回呼:

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

如果你點擊返回,你會得到:

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

如果旋轉螢幕,活動將被拆除並重新創建,您將獲得完整的循環。您可以透過檢查isFinishing()判斷這是快速重啟。啟動/停止活動的速度可能如此之快,以至於surfaceCreated()onPause()之後發生。

如果點擊電源按鈕使螢幕空白,則只會得到onPause() ,而不會得到surfaceDestroyed() 。表面保持活動狀態,渲染可以繼續。如果您繼續請求,您可以繼續獲得 Choreographer 事件。如果您的鎖定畫面強制不同的方向,當裝置解鎖時,您的活動可能會重新開始。否則,您可以從螢幕空白中出來,並獲得與以前相同的表面。

線程的壽命可以與表面或活動相關,具體取決於螢幕變黑時您希望發生的情況。執行緒可以在 Activity 啟動/停止時或表面建立/銷毀時啟動/停止。

在 Activity 啟動/停止時啟動/停止執行緒與應用程式生命週期配合良好。您在onResume()中啟動渲染器線程並在onStop()中停止它。建立和配置執行緒時,有時表面已經存在,有時則不存在(例如,使用電源按鈕切換螢幕後它仍然處於活動狀態)。在線程中初始化之前,您必須等待表面被創建。您無法在surfaceCreate()回呼中進行初始化,因為如果未重新建立表面,它就不會再次觸發。相反,查詢或快取表面狀態,並將其轉發到渲染器執行緒。

讓線程在表面創建/銷毀上啟動/停止效果很好,因為表面和渲染器在邏輯上是交織在一起的。創建表面後啟動線程,這避免了一些線程間通訊問題;並且簡單地轉發表面創建/更改的訊息。為了確保渲染在螢幕變為空白時停止並在螢幕取消空白時恢復,請告訴 Choreographer 停止呼叫幀繪製回調。如果渲染器執行緒正在運行, onResume()將恢復回呼。但是,如果您根據幀之間經過的時間製作動畫,則在下一個事件到達之前可能會有很大的間隙;使用明確暫停/恢復訊息可以解決此問題。

這兩個選項,無論執行緒的生命週期與 Activity 還是表面相關,都專注於渲染器執行緒的配置方式以及它是否正在執行。一個相關的問題是當活動被終止時從執行緒中提取狀態(在onStop()onSaveInstanceState()中);在這種情況下,將執行緒的生命週期與活動連結起來效果最好,因為在加入渲染器執行緒後,無需同步原語即可存取渲染執行緒的狀態。

GL表面視圖

GLSurfaceView類別提供了用於管理 EGL 上下文、執行緒間通訊以及與活動生命週期互動的幫助程式類別。您不需要使用 GLSurfaceView 來使用 GLES。

例如,GLSurfaceView 建立一個用於渲染的執行緒並在其中配置 EGL 上下文。當活動暫停時,狀態會自動清除。大多數應用程式不需要了解有關 EGL 的任何資訊即可將 GLES 與 GLSurfaceView 結合使用。

在大多數情況下,GLSurfaceView 可以讓 GLES 的使用變得更容易。在某些情況下,它可能會妨礙。