原理
可以簡單理解為通過底層軟件代碼,將 CPU 不擅長的圖形計算轉(zhuǎn)換為 GPU 專用指令,由 GPU 完成。
當目標 API 級別大于等于 14 時,硬件加速默認開啟。
控制硬件加速
我們可以在以下 4 個級別控制硬件加速:
-
Application
在清單文件種,加入以下屬性,為整個應用啟用硬件加速:
<application android:hardwareAccelerated="true" ...>
-
Activity
在清單文件中對應的 <activity> 標簽下添加下面代碼:
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
-
Window
window 級別不能停用硬件加速。
window.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)
-
View
View 不能啟用硬件加速,只能停用硬件加速,如下:
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
DisplayList
DisplayList 是一個基本繪制元素,包含元素原始屬性(位置、尺寸、角度、透明等),當 view 的一些屬性改變(scale 、rotate、alpha、translate),只需把屬性更新給 GPU,不需要生成新的 DisplayList。
RenderNode
一個 RenderNode 包含若干個 DisplayList,通常一個 RendeNode 對應一個 View,包含 View 及其 View 的所有 DisplayList。
軟件繪制與硬件繪制
軟件繪制
繪制內(nèi)容會被 CPU 轉(zhuǎn)換成實際的像素(由 Bitmap 來承載),然后直接渲染到屏幕上。-
硬件繪制
繪制的內(nèi)容會轉(zhuǎn)換成 GPU 的操作保存下來(由 DisplayList 來承載),再交給 GPU 來操作。
來自美團
場景1中,無論是否開啟硬件加速,遍歷 view 樹并都會走 Draw 路徑。硬件加速后 Draw 路徑不做實際繪制工作,只是構建 DisplayList,復雜的繪制計算被 GPU 分擔。
場景2 中,TextView 設置前后尺寸位置不變,不會觸發(fā)重新 Layout。
- 軟件繪制中,TextView 所在的區(qū)域及為臟區(qū),由于 TextView 有透明區(qū)域,遍歷 View 樹的過程中,和臟區(qū)重疊的多數(shù) View 都要重繪,包括與之重疊的的兄弟節(jié)點和他們的父節(jié)點,不要繪制的 View 在 draw(Canvas canvas, ViewGroup parent, long drawingTime) 方法中判斷直接返回。
判斷 View 是否經(jīng)過硬件加速
-
View.isHardwareAccelerated()
當 view 已經(jīng)附著到啟用硬件加速的 window 后,這個方法只會返回 true,即使在設置了 view.setLayerType(View.LAYER_TYPE_SOFTWARE, null) 之后。如果 view 已經(jīng)附著到關閉硬件加速的 window 中,則只會返回 false。這種判斷方法并不靠譜。 - Canvas.isHardwareAccelerated(),如果 Canvas 經(jīng)過硬件加速,則其會返回 true。在繪制代碼中如果要判斷 view 是否啟用硬件加速時,應該用這個方法而不是上面那個。
ViewLayer
View Layer 又稱離屏緩沖,它的作用是單獨開辟一塊地方來繪制 view,
前面提到 View 不能啟用硬件加速,只能停用硬件加速具體展開情況如下:
如果 View 附著的 window 啟用硬件加速,則可通過 view.setLayerType(View.LAYER_TYPE_SOFTWARE, null) 關閉(暫停)硬件加速,此后還是可以通過 view.setLayerType(View.LAYER_TYPE_SOFTWARE, null) 或者 view.setLayerType(View.LAYER_TYPE_NONE, null) 來恢復硬件加速的,因為 Window 一開始是支持硬件加速的,故不能說 view 能啟動硬件加速。
如果 View 附著 的 window 沒有啟用硬件加速(Activity 關閉了硬件加速),無論 view 設置 LayerType 為何值,都不能啟用硬件加速。
當我們設置了 View Layer 后,繪制操作會被緩存下來,而且緩存的的是最終的繪制結果。這樣,View 的重繪效率進一步提升:只要繪制的內(nèi)容沒有變,那么無論是軟件繪制(CPU)還是硬件繪制(GPU),它們都不用重新計算,只用之前的緩存的繪制結果即可。(可以對標圖片加載緩存來理解。)
硬件加速與動畫
動畫在 App 中是必不可少的,而動畫又是相對消耗性能的,卡頓的動畫嚴重影響用戶體驗。幸運的是,我們可以通過設置 Hardware Layer 的方式提升動畫的效率,代碼如下:
view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
ObjectAnimator.ofFloat(view, "rotationY", 180f).apply {
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
view.setLayerType(View.LAYER_TYPE_NONE, null)
}
})
start()
}
設置 Hardware Layer 會占用視頻內(nèi)存,因此我們應該只在動畫播放期間啟用。之所以能提高動畫效率是因為在進行移動、旋轉(zhuǎn)、縮放、透明度(無需調(diào)用 invalidate)的動畫時候,View 本身并沒有發(fā)生改變,只是它的位置或者角度改變了,而這種改變是可以由 GPU 通過簡單計算就完成的,并不需要重繪整個 View。與之對應的,如果我們開啟的是 自定義屬性繪制的動畫或者手動調(diào)用了 invalidate,這種設置方式是沒有用的。
