Android 優(yōu)化之硬件加速

原理

可以簡單理解為通過底層軟件代碼,將 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. 場景1中,無論是否開啟硬件加速,遍歷 view 樹并都會走 Draw 路徑。硬件加速后 Draw 路徑不做實際繪制工作,只是構建 DisplayList,復雜的繪制計算被 GPU 分擔。

  2. 場景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,這種設置方式是沒有用的。

參考

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容