根據博客?Android-View 繪制原理(01)-JAVA層分析_android view draw原理分析-CSDN博客
提出了我的疑問??Canvas?RenderNode?updateDisplayListDirty 這些東西的關系
您的理解在基本方向上是對的,但讓我詳細解釋一下 Android 中 updateDisplayListDirty
、指令集合、Canvas 和 RenderNode 之間的關系:
RenderNode 與渲染過程
在 Android 的渲染系統中:
- RenderNode:
- 是一個存儲渲染操作的容器
- 包含了視圖的繪制命令和屬性信息
- 可以被緩存和重用,提高渲染效率
- DisplayList:
- 實際上是 RenderNode 內部的繪制命令列表
- 存儲了一系列需要執行的渲染指令
- 通過
updateDisplayListDirty
方法更新這些指令
- Canvas:
- 是應用層與渲染系統交互的接口
- 提供了各種繪制方法(drawRect、drawText 等)
- 當我們調用 Canvas 的繪制方法時,它會轉換為渲染指令
updateDisplayListDirty 與 Canvas 的關系
updateDisplayListDirty
方法在 View 類中實現,主要作用是:
- 創建或獲取一個 RenderNode
- 使用 DisplayListCanvas(特殊的 Canvas 實現)錄制繪制指令
- 當視圖調用 onDraw() 方法時,它使用提供的 Canvas 執行繪制
- 這些繪制命令被記錄到 DisplayList 中
- 最終生成或更新 RenderNode
Canvas 和 DisplayList 不是可替代關系,而是協作關系:
- Canvas 是繪制接口,應用通過它表達"畫什么"
- DisplayList 是指令集合,記錄"如何畫"
- RenderNode 是整個結構的容器,包含了 DisplayList 和其他渲染屬性
所以不是"有一個就可以了",而是它們在渲染管線中扮演不同角色:Canvas 是應用用來繪制的工具,而 DisplayList 是記錄這些繪制指令的容器,兩者共同構成了 Android 的高效渲染系統。
?看完后我的疑問變成了,既然?updateDisplayListDirty
?是一個指令集合 在硬件加速的情況下,一個渲染當VSYNC信號來了,進行三大流程進行獲取surfaceControl然后進行 繪制,繪制時候先新建RenderNode 然后獲取?RenderNode 的?
RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
RecordingCanvas 然后把它變成后續的?Canvas
最后??
public void endRecording() {if (mCurrentRecordingCanvas == null) {throw new IllegalStateException("No recording in progress, forgot to call #beginRecording()?");}RecordingCanvas canvas = mCurrentRecordingCanvas;mCurrentRecordingCanvas = null;canvas.finishRecording(this);canvas.recycle();}
finishRecording 把 當前的?RenderNode 傳過去 然后進行繪制
那么我的問題是 之后surfaceFlinger是否繪制的是指令集合?是否是當RecordingCanvas? 執行一次生成一個指令就繪制一部分還是?finishRecording 后一起進行繪制?
您的問題涉及到 Android 渲染流水線的關鍵細節,我來詳細解釋:
finishRecording 與渲染流程
在 Android 的硬件加速渲染流程中,當 finishRecording()
被調用時:
- 指令集完成階段:
finishRecording()
標志著 RecordingCanvas 已經完成所有繪制命令的記錄- 此時,所有來自
onDraw()
的繪制操作已經被轉換為渲染指令并存儲在 RenderNode 中 - 這些指令并不會立即觸發繪制
- 批處理渲染模式:
- SurfaceFlinger 不會 在每條指令生成后立即渲染
- 系統采用批處理模式,收集完所有指令后一次性處理
- 這種方式大大提高了渲染效率,避免了頻繁的狀態切換和上下文切換
- 渲染時機:
- 在
ViewRootImpl.performTraversals()
完成后,會調用ThreadedRenderer.draw()
- 此時,所有視圖的 RenderNode 都已準備就緒
- 系統會將這些渲染命令通過 HWUI 傳遞給 GPU
- SurfaceFlinger 會在下一個 VSYNC 信號到來時合成并顯示結果
- 在
- SurfaceFlinger 處理:
- SurfaceFlinger 接收的是已經處理過的圖層數據
- 它主要負責合成多個應用的圖層,而不是執行具體的繪制指令
- 實際的指令執行發生在 GPU 上,在應用進程的渲染線程中
所以,回答您的核心問題:系統是在 finishRecording()
后,將完整的指令集合一次性提交給渲染管線進行處理,而不是每生成一條指令就執行一次渲染。這種批處理模式是現代圖形渲染系統的普遍做法,可以最大限度地減少 GPU 狀態切換,提高渲染效率。
需要注意的是,RecordingCanvas 記錄的指令集和 SurfaceFlinger 處理的內容在概念上有區別——前者是應用內的繪制指令,后者處理的是已經渲染好的圖層。
?
那這些指令在哪被處理?
這些渲染指令的處理發生在多個層次的渲染管線中,讓我詳細解釋這個流程:
渲染指令的處理路徑
- 應用進程中的處理:
- 當
finishRecording()
完成后,渲染指令首先在應用自己的渲染線程中處理 ThreadedRenderer
類負責協調這個過程- 具體處理由 HWUI (Hardware User Interface) 庫在原生層完成
- 在這一階段,渲染指令被轉換為 GPU 可以理解的命令(如 OpenGL ES 或 Vulkan 調用)
- 當
- RenderThread 中的執行:
- Android 使用專門的 RenderThread(渲染線程)處理這些指令
- 這個線程與主線程分離,可以并行處理渲染工作
- 渲染指令在這里被組織成命令緩沖區(Command Buffer)
- 這些命令最終會通過圖形驅動提交給 GPU
- GPU 執行階段:
- GPU 接收這些渲染命令并執行實際的像素處理
- 渲染結果被寫入到應用的 Surface 對應的圖形緩沖區中
- 這個過程完全由 GPU 硬件加速完成
- SurfaceFlinger 的角色:
- SurfaceFlinger 不處理原始渲染指令,而是處理已渲染好的圖層
- 它從各個應用獲取已經渲染好的圖形緩沖區
- 然后進行合成(Composition)操作,決定不同應用的圖層如何疊加顯示
- 最后將合成結果提交給顯示硬件
具體源碼路徑
如果您想在源碼中追蹤這個處理過程:
ThreadedRenderer.nSyncAndDrawFrame()
是關鍵的入口點,它通過 JNI 調用原生層代碼- 在原生層,
android_view_ThreadedRenderer.cpp
包含了相關實現 - 進一步深入到
libs/hwui/
目錄,特別是RenderProxy
和RenderThread
相關類 - 最終的 GPU 命令提交通過圖形驅動 API(如 OpenGL ES 或 Vulkan)完成
所以,渲染指令主要在應用自己的渲染線程中被處理和執行,而不是在 SurfaceFlinger 中。SurfaceFlinger 的工作是在所有應用都完成各自的渲染后,組合這些結果并顯示到屏幕上。