最近開發遇到問題,ImageView設置visibility未顯示。這時View已經post到主線程顯示,并且父view可以正常顯示。懷疑是系統顯示問題于是對顯示系統進行了學習梳理。
本文將從三個方面介紹Android 圖形系統。
一、圖形系統簡介
1.1 Android 繪制基礎
- 系統繪制的是什么?
圖形緩沖區,也叫Buffer 。應用層的View樹最終會轉換成Buffer,置于BufferQueue中被繪制。 - 繪制的位置在哪里?
應用端會把一切內容渲染到surface上,最終顯示到LCD/OLED顯示屏 -
如何把圖像繪制到屏幕?
繪制任務由應用發起,通過跨進程方式把Buffer傳到FW層,由FW層中的SurfaceFlinger服務調用Linux 硬件驅動最終繪制到硬件屏幕上。
1.2 Android 圖形引擎
圖形系統提供繪圖和圖形處理支持。
Android 框架提供了各種用于 2D 和 3D 圖形渲染的 API、圖片解碼庫,以及各種Driver支持。
? 繪圖API:2D引擎 Skia,3D引擎 OpenGL ES,RenderScript,OpenCV和Vulkan。
? 圖片解碼庫:jpg,png,gif等。
應用開發者可通過三種方式將圖像繪制到屏幕:
? Canvas : 2D圖形API,Android View樹實際的繪制者。
? OpenGL ES : 嵌入式設備的OpenGL 三維圖形API子集。
? Vulkan :跨平臺的2D和3D繪圖引擎,Android 7.0后支持,NDK。
1.3 圖形系統架構
整個圖形系統架構是一個生產者和消費者模式,五層依次介紹:
- Image Stream Producers :
? view每次執行lockCanvas->draw->unlockCanvas,會存入一幀數據進入BufferQueue中 - Native Framework:
? Buffer的生成和BufferQueue數據跨進程傳遞 - WindowManager:
? 計算窗口大小,位置等,同時也會將相應的參數設置給SurfaceFlinger,比如Window的z-order和大小等。 - Image stream consumers :
? SurfaceFlinger,消耗當前可見的 Surface,作為Layer的管理著,同是也是BufferQueue的消費者,當每個Layer的生產者繪制完一幀時,會通知SurfaceFlinger。 - HAL:
? Gralloc: 圖形內存分配器,分配圖像生產方請求的內存
? Hardware Composer:硬件混合渲染器,合成SurfaceFlinger里面的Layer,并顯示
二、圖形組件與繪制流程
2.1 2D/3D繪圖流程
2D繪制:Canvas api / view 的子類 (button ,list)/自定義view
3D繪制:應用直接使用OpenGL 接口繪制圖形(PixelFlinger對應的是openGl 1.0 ,GUP driver 對應的是2.0和3.0)
所有情況下的繪圖都渲染到一個包含 GraphicBuffer的Surface上,當一塊 Surface 顯示在屏幕上時,就是用戶所看到的窗口。
2.2 Canvas/ Skia/ hwui/ OpenGL ES
? Canvas:畫布,2D圖形API,Android View樹實際的渲染者。
? Skia繪制:Android4.0之前默認使用,主線程通過CPU完成繪圖指令操作,在復雜場景下單幀容易超過16ms導致卡頓。
注意:并非所有 2D 繪制操作都支持硬件加速,可能會影響部分自定義視圖或繪制調用。詳情見:Android硬件加速
? OpenGL ES : OpenGL 三維圖形 API 的子集,針對手機、PDA和游戲主機等嵌入式設備而設計。在異步線程直接通過OpenGL ES進行渲染,一般適用于游戲、視頻播放等獨立場景
2.3 圖形組件WMS
WindowManagerService(WMS)窗口管理服務,管理系統中所有的窗口。
? 管理window (view的容器)
? Window與surface對應,一塊顯示區域。添加一個window,就是 WMS 為其分配一塊 Surface 的過程。
Android setContentView就是將View設置到widow上。
WMS角色在整個流程的位置:
這里有一個wm命令可以看到window的信息:對手機分辨率、像素密度、顯示區域進行設置的命令
2.4 Surface與相關的view
Google 在Android source官網提示:
“ What every developer should know about surfaces, SurfaceHolder, EGLSurface, SurfaceView, GLSurfaceView, SurfaceTexture, TextureView, SurfaceFlinger, and Vulkan.”
這里就對這些控件進行簡單介紹:
Surface
Surface : Handle onto a raw buffer that is being managed by the screen compositor.
Surface 對應一塊屏幕緩沖區。生產者是: SurfaceTexture、MediaRecorder 等,消費者是: OpenGL、MediaPlayer 或 CameraDevice等。每個window對應一個Surface。Canvas或OpenGL ES等最終都渲染到Surface上。
? Flutter在Android平臺上也是直接渲染到Surface。例如:一個Activity/Dialog都是一個Surface,它承載了上層的圖形數據,與SurfaceFlinger側的Layer相對應。
Canvas rendering:
Canvas(畫布)實現由 Skia 圖形庫提供。為了確保兩個客戶端不會同時更新某個緩沖區,使用以下命令處理畫布鎖:
SurfaceView和SurfaceHolder
SurfaceView
使用雙緩沖機制,有自己的 surface,View只是一個透明的占位符,Surface可以在后臺線程中繪制。雙緩沖機制提高渲染效率,獨立線程
繪制,提升流暢性。適合一些場景:需要界面迅速更新、UI繪制時間長、對幀率要求較高的情況。
SurfaceHolder
提供訪問和控制Surface 相關的方法 。通過SurfaceView的getHolder()函數可以獲取SurfaceHolder對象,Surface 就在SurfaceHolder對象內。
addCallback(SurfaceHolder.Callbackcallback) /Canvas lockCanvas() /unlockCanvasAndPost(Canvascanvas)
SurfaceTexture/ GLSurfaceView/ TextureView
SurfaceTexture: Surface 和 OpenGL ES (GLES) 紋理(Texture)的組合。將圖像流轉為 OpenGL 外部紋理。
TextureView:持有 SurfaceTexture,將圖像處理為 OpenGL 紋理更新到 HardwareLayer。
GLSurfaceView:加入 EGL 管理,自帶 GL 上下文和 GL 渲染線程
這些View通常涉及到Android音視頻相關,需要高效的渲染能力。如下面的SurfaceTexture在camera中的應用。
GraphicBuffer / BufferQueue
GraphicBuffer
簡稱Buffer, 一個Buffer包含一幀圖像,Buffer由gralloc分配和回收。Buffer 屬性包含:width, height, format, usage等
BufferQueue
BufferQueue 的引入是為了解決顯示和性能問題。
? Surface屬于APP進程,Layer屬于系統進程,如果它們之間只用一個Buffer,會存在顯示和性能問題。
? 一些Buffer用于繪制,一些Buffer用于顯示,雙方處理完之后,交換一下Buffer,提高效率。
? BufferQueue中包含多個Buffer對象。
Android圖形系統包含了兩對生產者和消費者模型,它們都通過BufferQueue進行連接:
1.Canvas和OpenGL ES生產圖形數據,SurfaceFlinger消費圖形數據。
2.SurfaceFlinger合成所有圖層的圖形數據,Display顯示合成結果。
SurfaceFlinger
code:frameworks/native/services/surfaceflinger
? Surface表示APP進程的一個窗口,承載了窗口的圖形數據。
? SurfaceFlinger是系統進程合成所有窗口的系統服務,負責合成所有Surface提供的圖形數據,然后送顯到屏幕。
? SurfaceFlinger既是上層應用的消費者,又是Display的生產者,起到了承上啟下的作用。
數據流:
合成示意圖:
SurfaceFlinger的啟動流程:
Surface Flinger 服務由init進程啟動,一個高優先級的本地守護進程。SurfaceFlinger啟動流程
Android系統的啟動流程:
可以看到SurfaceFlinger的啟動是和Zygote進程啟動位于同一流程中。
SurfaceFlinger API
Vsync機制
在介紹Vsync機制之前先介紹兩個重要概念:
屏幕刷新率:屏幕每秒鐘可以刷新多少次。60HZ刷新率,16.7ms刷新一次。(120HZ/8.3ms),硬件指標。
GPU 繪制幀率:GPU 每秒能夠合成繪制多少幀。
軟件層觸發 View 繪制的時機是隨機的,當下一次屏幕刷新時,屏幕從 Frame Buffer 中拿到的數據還是“幀1”的數據,導致“丟幀”。
每隔 16ms 硬件層發出 vsync 信號,應用層接收到此信號后會觸發UI 的渲染流程,同時 vsync 信號也會觸發 SurfaceFlinger 讀取Buffer 中的數據,進行合成顯示到屏幕上。
總結:Vsync機制將 CPU 和 GPU 的開始時間與屏幕刷新強行拖拽到同一起跑線
三、Graphics問題總結
Android提供的Graphics流程相對比較復雜對其進行具象后的流程如下兩張圖所示: