Android的UI底層是用CPU繪圖的還是GPU繪圖的呢?以及surfaceview,window,普通view是如何實現的?

補充下描述,Android中的surfaceview以及其他的textview,image view在底層實現上究竟有什麼區別?在自定義view時有個canvas介面,這個又和surface有什麼關係?


前幾天提問的,沒有人回答,自己查了下,現在自己把這個問題回答下。

首先,對於CPU與GPU繪製的問題,根據google的文檔

http://developer.android.com/guide/topics/graphics/hardware-accel.html

安卓有2種繪製模型:

一.軟體繪製模型,這裡由CPU主導繪圖,視圖按照以下2個步驟繪圖。

  1. 讓視圖結構(view hierarchy)失效。
  2. 繪製整個視圖結構。

當應用程序需要更新它的部分UI時,都會調用內容發生改變的View對象的invalidate()方法。無效(invalidation)消息請求會在View對象層次結構中傳遞,以便計算出需要重繪的屏幕區域(臟區)。然後,Android系統會在View層次結構中繪製所有的跟臟區相交的區域。但是,這種方法有兩個缺點:

1. 繪製了不需要重繪的視圖(與臟區域相交的區域)

2. 掩蓋了一些應用的bug(由於會重繪與臟區域相交的區域)

注意:在View對象的屬性發生變化時,如背景色或TextView對象中的文本等,Android系統會自動的調用該View對象的invalidate()方法。

二.硬體加速繪製模型,這裡由GPU主導繪圖,視圖按照以下3個步驟繪圖。

  1. 讓視圖結構失效。
  2. 記錄和更新顯示列表(Display List)。
  3. 繪製顯示列表。

  這種模式下,Android系統依然會使用invalidate()方法和draw()方法來請求屏幕更新和展現View對象。但Android系統並不是立即執行繪製命令,而是首先把這些View的繪製函數作為繪製指令記錄一個顯示列表中,然後再讀取顯示列表中的繪製指令調用OpenGL相關函數完成實際繪製。另一個優化是,Android系統只需要針對由invalidate()方法調用所標記的View對象的臟區進行記錄和更新顯示列表。沒有失效的View對象就簡單重用先前顯示列表記錄的繪製指令來進行簡單的重繪工作。

  使用顯示列表的目的是,把視圖的各種繪製函數翻譯成繪製指令保存起來,對於沒有發生改變的視圖把原先保存的操作指令重新讀取出來重放一次就可以了,提高了視圖的顯示速度。而對於需要重繪的View,則更新顯示列表,然後再調用OpenGL完成繪製。

  在這種繪製模型下,我們不能依賴一個視圖與臟區(dirty region)相交而導致它的draw()方法被自動調用,所以必須要手動調用該視圖的invalidate()方法去更新顯示列表。如果忘記這麼做可能導致視圖在改變後不會發生變化。

硬體加速提高了Android系統顯示和刷新的速度,但它也不是萬能的,它有三個缺陷:

1. 兼容性(部分繪製函數不支持或不完全硬體加速)

2. 內存消耗(OpenGL API調用就會佔用8MB,而實際上會佔用更多內存)

3. 電量消耗(GPU耗電)

第二個問題,關於SurfaceView,View,Canvas等問題,

參考安卓framework開發者Dianne Hackborn在stackoverflow給出的定義,

http://stackoverflow.com/questions/4576909/understanding-canvas-and-surface-concepts#answers

以及相關的PPT,下載鏈接如下:

https://sites.google.com/site/androidcontentfromchet/downloads/GraphicsandAnimations-Devoxx2010.pdf?attredirects=0

首先亮圖,然後再給出定義。

  1. 表面(Surface): Surface就是指向顯存的一個物體,用來被繪製到屏幕上,所有你能看見的Window都擁有可以在上面繪製的Surface,在安卓中,系統使用Surface Flinger服務來把Surface按照正確的深度信息渲染到最終的屏幕上。一個Surface一般擁有超過一個(通常是2個) buffer 單元(back buffer 與 front buffer),系統通常在back buffer中進行渲染,完成之後與front buffer交換,這樣顯示到屏幕上,以實現流暢顯示的效果。
  2. 窗口(Window): Window擁有一個Surface,在Surface里繪製Window里的內容。一個application通過Windows Manager來創建窗口,Windows Manager為每一個窗口創建Surface來讓application在上面繪製各種物體。
  3. 視圖(View):視圖就窗口裡的UI元素,一個窗口只擁有一個View Hierarchy,這些View Hierarchy提供了窗口裡的所有表現。當一個窗口需要重新繪製時(比如一個View invalidate自己),鎖定Surface,並返回一個Canvas用來在上面繪製,如上圖所示,在view hierarchy樹向下傳遞Canvas,來繪製每個view。這一切都完成後,Surface被解鎖,並通過Surface Flinger交換前後Buffer來顯示到屏幕上。
  4. Canvas:Canvas是Surface繪圖時返回的一個介面,並提供一些繪圖api,用來進行實際的繪圖操作。目前Canvas可以繪製在bitmap或者openGL container上。
  5. SurfaceView: SurfaceView是View的一個特殊子類,它擁有專有的Surface,使application可以直接在上面繪製(普通的view hierarchy必須共享窗口唯一的surface)。這其中的原理很簡單,SurfaceView就是請求Window Manager創建一個新窗口,並改變窗口之間的深度信息來顯示。如果SurfaceView的Window顯示在主窗口的後面,surfaceview將主窗口相應的位置設置成透明來使可見。

總結一下,一個activity擁有一個window(用來繪製它的UI), 並且一個Window只有一個Surface和View hierarchy來繪製,SurfaceView實質上是創建了一個新的窗口,所以擁有自己獨立的Surface,可以直接繪製在上面。


恰好前幾天總結了一張流程圖,如下:


從官網挖一張圖,基本能說明Window、Surface、OpenGL之間的關係:

http://source.android.com/devices/graphics/index.html

正如@沙茶醬所說,Android有兩種繪製模式—— 依賴CPU實現的軟體渲染,依賴GPU實現的硬體渲染。

先回答第1個疑問:UI底層是GPU繪製

Android繪製時可採用兩種方式:Canvas和OpenGL。android.graphics.Canvas 是Android中的2D繪製API,如果不開硬體加速,就是軟體渲染,如果開了硬體加速,就是硬體渲染,內部是通過OpenGLRenderer這個類將Canvas的繪製過程交給GPU的。而Android 4.0之後默認開啟了硬體加速,不過目前4.0之前的手機已經佔有率已經很低。

OPenGL用於3D繪圖,底層是GPU渲染。

第2個疑問:Surfaceview以及其他的Textview,Imageview在底層實現上究竟有什麼區別?

第3個疑問:在自定義view時有個canvas介面,這個又和surface有什麼關係?

先說Android中有3類窗口:

應用窗口(Application Window):對應一個Activity,Activity中Window的實現類是PhoneWindow,這也是目前Window類的唯一實現;

子窗口(Sub Window):必須依附於任何類型的父窗口,例如PopupWinow、Dialog等;

系統窗口(System Window): 系統設計的,不依附於任何應用的窗口,比如說,狀態欄(Status Bar), 導航欄(Navigation Bar), 壁紙(Wallpaper), 來電顯示窗口(Phone),鎖屏窗口(KeyGuard), 信息提示窗口(Toast), 音量調整窗口,滑鼠游標等等。

各種Textview,Imageview等控制項構成view hierarchy,view hierarchy會交給一個組件,例如將布局文件設置到Activity,view hierarchy通過這個組件對應了一個Window(例如Activity中的PhoneWindow),而Window中持有一個Surface對象,就是真實繪製表面。這個Suface對象最終作為數據元,通過匿名共享內存機制進行跨進程通訊,交給SurfaceFlinger。

這裡再挖2個圖:

Android應用程序與SurfaceFlinger服務的關係概述和學習計劃 - 老羅的Android之旅 - 博客頻道 - CSDN.NET

整個繪製流程都是在主線程即UI線程中跑的,各種measure和draw函數都是在主線程中。

而Suraceview雖然也嵌入了view hierarchy,但是卻不是在主線程繪製,而是在子線程中繪製,為什麼要提供Suraceview呢?主要用於耗時繪製的場景,例如視頻、動畫等。


CPU主要做複雜的邏輯運算,GPU做反覆重複的簡單運算。Android繪製中,Display List和view hierarchy的更新和組裝都是邏輯運算由CPU完成,最後的柵格化傻瓜重複操作交給GUP來完成。這個理解對不?


cpu繪圖採用的skia.硬體加速直接調用opengl繪圖。


不錯,正打算用 SurfaceView 寫音頻的音頻圖。


推薦閱讀:

智能手機感測器里的線性加速度器、陀螺儀、重力感應儀,有什麼區別?
安卓系統既然是開源的系統,為什麼穩定程度一直保證不了?
OkHttp在安卓中的使用?
你遇到過哪些高質量的Android面試?

TAG:Android開發 | OpenGL |