Android 應用程序框架 UI 基於以View開頭的對象層次結構。所有 UI 元素都經過一系列測量和將它們放入矩形區域的佈局過程。然後,所有可見的視圖對像都被渲染到一個由 WindowManager 在應用程序被帶到前台時設置的表面上。應用程序的 UI 線程每幀執行佈局和渲染到緩衝區。
表面視圖
SurfaceView 是一個組件,可用於在視圖層次結構中嵌入額外的複合層。 SurfaceView 採用與其他視圖相同的佈局參數,因此可以像任何其他視圖一樣對其進行操作,但 SurfaceView 的內容是透明的。
當您使用外部緩衝區源(例如 GL 上下文或媒體解碼器)進行渲染時,您需要從緩衝區源複製緩衝區以在屏幕上顯示緩衝區。使用 SurfaceView 使您能夠做到這一點。
當 SurfaceView 的視圖組件即將變得可見時,框架會要求 SurfaceControl 從 SurfaceFlinger 請求新的表面。要在創建或銷毀表面時接收回調,請使用SurfaceHolder接口。默認情況下,新創建的表面放置在應用程序 UI 表面的後面。您可以覆蓋默認的 Z 順序以將新曲面放在頂部。
在需要渲染到單獨的表面的情況下,使用 SurfaceView 渲染非常有用,例如當您使用 Camera API 或 OpenGL ES 上下文進行渲染時。使用 SurfaceView 進行渲染時,SurfaceFlinger 直接將緩衝區組合到屏幕上。如果沒有 SurfaceView,您需要將緩衝區合成到屏幕外表面,然後合成到屏幕上,因此使用 SurfaceView 進行渲染消除了額外的工作。使用 SurfaceView 渲染後,使用 UI 線程與活動生命週期協調,並在需要時調整視圖的大小或位置。然後,Hardware Composer 將應用程序 UI 和其他層混合在一起。
新的 Surface 是 BufferQueue 的生產者端,其消費者是 SurfaceFlinger 層。您可以使用任何可以提供 BufferQueue 的機制來更新表面,例如表面提供的 Canvas 函數、附加 EGLSurface 並使用 GLES 在表面上繪圖,或者配置媒體解碼器來寫入表面。
SurfaceView 和活動生命週期
使用 SurfaceView 時,從主 UI 線程以外的線程渲染表面。
對於具有 SurfaceView 的活動,有兩個獨立但相互依賴的狀態機:
- 應用程序
onCreate
/onResume
/onPause
- 表面創建/更改/銷毀
當活動開始時,您會按以下順序獲得回調:
-
onCreate()
-
onResume()
-
surfaceCreated()
-
surfaceChanged()
如果你點擊返回,你會得到:
-
onPause()
-
surfaceDestroyed()
(在表面消失之前調用)
如果您旋轉屏幕,活動將被拆除並重新創建,您將獲得完整的循環。您可以通過檢查isFinishing()
來判斷它是快速重啟。可以如此快速地啟動/停止活動,以至於surfaceCreated()
在onPause()
之後發生。
如果您點擊電源按鈕使屏幕空白,您只會得到onPause()
而沒有surfaceDestroyed()
。表面保持活動狀態,並且可以繼續渲染。如果您繼續請求,您可以繼續獲得 Choreographer 事件。如果您有一個強制不同方向的鎖定屏幕,則您的活動可能會在設備未清屏時重新啟動。否則,您可以以與以前相同的表面從屏幕空白中出來。
線程的生命週期可以與表面或活動相關聯,這取決於您希望在屏幕變為空白時發生什麼。線程可以在 Activity 啟動/停止或表面創建/銷毀時啟動/停止。
讓線程在 Activity 啟動/停止時啟動/停止適用於應用程序生命週期。您在onResume()
中啟動渲染器線程並在onStop()
中停止它。在創建和配置線程時,有時表面已經存在,有時不存在(例如,在使用電源按鈕切換屏幕後它仍然處於活動狀態)。在線程中初始化之前,您必須等待創建表面。您無法在surfaceCreate()
回調中進行初始化,因為如果未重新創建表面,它將不會再次觸發。相反,查詢或緩存表面狀態,並將其轉發到渲染器線程。
在表面創建/銷毀上啟動/停止線程效果很好,因為表面和渲染器在邏輯上是交織在一起的。您在創建表面後啟動線程,這避免了一些線程間通信問題;並且表面創建/更改的消息被簡單地轉發。為確保在屏幕變黑時停止渲染並在屏幕變黑時恢復,請告訴 Choreographer 停止調用幀繪製回調。如果渲染器線程正在運行, onResume()
將恢復回調。但是,如果您根據幀之間經過的時間製作動畫,則在下一個事件到達之前可能會有很大的差距;使用顯式暫停/恢復消息可以解決此問題。
這兩個選項,無論線程的生命週期是綁定到 Activity 還是表面,都關注渲染器線程的配置方式以及它是否正在執行。一個相關的問題是當活動被殺死時從線程中提取狀態(在onStop()
或onSaveInstanceState()
中);在這種情況下,將線程的生命週期與活動聯繫起來效果最好,因為在加入渲染器線程之後,可以在沒有同步原語的情況下訪問渲染線程的狀態。
GLSurfaceView
GLSurfaceView類提供幫助類來管理 EGL 上下文、線程間通信以及與活動生命週期的交互。您無需使用 GLSurfaceView 即可使用 GLES。
例如,GLSurfaceView 創建一個渲染線程並在那裡配置一個 EGL 上下文。活動暫停時會自動清理狀態。大多數應用程序不需要了解有關 EGL 的任何信息即可將 GLES 與 GLSurfaceView 一起使用。
在大多數情況下,GLSurfaceView 可以使使用 GLES 變得更容易。在某些情況下,它可能會妨礙您。