圖片和圖形之硬件加速(7)

原文

概要


從Android 3.0(API級別11)開始,Android 2D渲染管道支持硬件加速,這意味着在View畫布上執行的所有繪圖操作都 使用GPU。由於啓用硬件加速所需的資源增加,您的應用程序將消耗更多的RAM。

如果您的目標API級別大於等於14,但也可以顯式啓用,則默認情況下會啓用硬件加速。如果您的應用程序僅使用標準視圖和 Drawables,則全局打開它不應導致任何不利的繪圖效果。但是,由於所有2D繪圖操作都不支持硬件加速,因此將其打開可能會影響某些自定義視圖或繪圖調用。問題通常表現爲不可見的元素,例外或錯誤渲染的像素。爲了解決這個問題,Android允許您在多個級別啓用或禁用硬件加速。請參閱控制硬件加速。

如果您的應用程序執行自定義繪圖,請在開啓硬件加速功能的實際硬件設備上測試您的應用程序以發現任何問題 在不支持的繪圖操作部分介紹了硬件加速,以及如何解決它們的已知問題。

另請參閱使用框架API 和Renderscript的OpenGL

控制硬件加速


您可以在以下級別控制硬件加速:

  • Application
  • Activity
  • Window
  • View

Application level
在Android清單文件中,將以下屬性添加到 標記中,以便爲整個應用程序啓用硬件加速: <application>

<application android:hardwareAccelerated="true" ...>

Activity level

如果您的應用程序在全球範圍內打開硬件加速時無法正常運行,您也可以控制它以進行單獨的活動。要在活動級別啓用或禁用硬件加速,可以使用android:hardwareAccelerated該元素的屬性。以下示例爲整個應用程序啓用硬件加速,但爲一個活動禁用它: <activity>

<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

Window level
如果您需要更細緻的控制,則可以使用以下代碼爲給定窗口啓用硬件加速:

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
注意:您目前無法在窗口級別禁用硬件加速。

View level
您可以在運行時使用以下代碼禁用單個視圖的硬件加速:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
注意:您目前無法在視圖級別啓用硬件加速。除禁用硬件加速外,視圖圖層還具有其他功能。有關其用途的更多信息,請參閱[查看圖層](https://developer.android.com/guide/topics/graphics/hardware-accel#layers)。

確定視圖是否是硬件加速的


對於一個應用程序來說,它是否有助於瞭解它當前是否是硬件加速的,特別是對於諸如自定義視圖之類的東西。如果您的應用程序執行了大量的自定義繪製,並且並非所有操作都得到了新渲染管道的正確支持,那麼這非常有用。

有兩種不同的方法來檢查應用程序是否是硬件加速的:

  • View.isHardwareAccelerated()true如果View連接到硬件加速窗口,則返回 。

  • Canvas.isHardwareAccelerated()true如果Canvas硬件加速則 返回

如果您必須執行此操作,請檢查您的繪圖代碼, Canvas.isHardwareAccelerated()而不是View.isHardwareAccelerated()在可能的情況下使用。當視圖附加到硬件加速窗口時,仍然可以使用非硬件加速畫布繪製視圖。例如,爲了緩存目的,將視圖繪製到位圖時會發生這種情況。

Android繪圖模型


當啓用硬件加速時,Android框架將利用新的繪圖模型,利用顯示列表將應用程序呈現在屏幕上。要充分了解顯示列表以及它們可能如何影響您的應用程序,瞭解Android如何在沒有硬件加速的情況下繪製視圖也很有用。以下各節介紹基於軟件和硬件加速的繪圖模型。

基於軟件的繪圖模型

在軟件繪圖模型中,視圖通過以下兩個步驟繪製:

  • 使層次結構失效
  • 繪製層次結構

每當應用程序需要更新其UI的一部分時,它就會invalidate()在任何已更改內容的視圖上調用(或其中一種變體)。無效消息一直傳播到視圖層次結構中,以計算需要重繪的屏幕區域(髒區域)。Android系統然後在與髒區相交的層級中繪製任何視圖。不幸的是,這個繪圖模型有兩個缺點:

* 首先,這個模型需要在每個繪製過程中執行大量的代碼。例如,如果您的應用程序調用invalidate()某個按鈕,並且該按鈕位於另一個視圖的頂部,則Android系統即使沒有更改也會重新繪製該視圖。

* 第二個問題是繪圖模型可以隱藏應用程序中的錯誤。由於Android系統在與髒區域相交的情況下重繪視圖,因此您更改的內容的視圖可能會重新繪製,即使invalidate()未對其調用也是如此。發生這種情況時,您正在依靠另一個視圖被無效以獲得正確的行爲。每次修改應用程序時,此行爲都會更改。因此,invalidate()無論何時修改影響視圖的繪圖代碼的數據或狀態,都應該始終調用自定義視圖。

注意:Android視圖會invalidate()在其屬性發生更改時自動調用,例如背景顏色或a中的文本TextView。

硬件加速繪圖模型

Android系統仍然使用invalidate()並draw()請求屏幕更新並呈現視圖,但以不同的方式處理實際的圖形。Android系統不是立即執行繪圖命令,而是將它們記錄在顯示列表中,顯示列表包含視圖層次結構繪圖代碼的輸出。另一個優化是Android系統只需要記錄和更新顯示列表,以查看通過invalidate() 調用標記爲髒的視圖。沒有失效的視圖可以通過重新發布之前記錄的顯示列表重新繪製。新的繪圖模型包含三個階段:

  1. Invalidate the hierarchy:使層次結構失效
  2. Record and update display lists:記錄和更新顯示列表
  3. Draw the display lists:繪製顯示列表

有了這個模型,就不能依賴與髒區域相交的視圖來draw()執行其方法。爲確保Android系統記錄視圖的顯示列表,您必須調用invalidate()。忘記這樣做會導致視圖在更改後看起來一樣。

使用顯示列表也有利於動畫性能,因爲設置特定的屬性(如alpha或旋轉)不需要使目標視圖無效(它會自動完成)。此優化還適用於帶有顯示列表的視圖(應用程序硬件加速時的任何視圖)。例如,假設有一個LinearLayout包含ListView上面的a Button。顯示列表LinearLayout如下所示:

  • DrawDisplayList(ListView)
  • DrawDisplayList(Button)

現在假設你想改變ListView不透明度。調用後setAlpha(0.5f)的ListView,顯示列表現在包含這樣的:

  • SaveLayerAlpha(0.5)
  • DrawDisplayList(ListView)
  • Restore
  • DrawDisplayList(Button)

複雜的繪圖代碼ListView沒有執行。相反,系統只更新了更簡單的顯示列表LinearLayout。在未啓用硬件加速的應用程序中,列表及其父項的繪圖代碼將再次執行。

不支持的繪圖操作

當硬件加速時,2D渲染管線支持最常用的 Canvas繪圖操作以及許多較少使用的操作。支持所有用於渲染Android應用程序的繪圖操作,默認窗口小部件和佈局以及反射和平鋪紋理等常見高級視覺效果。

下表介紹了各個API級別的各種操作的支持級別:
圖片和圖形之硬件加速(7)

畫布縮放

硬件加速的二維渲染流水線首先被構建以支持未縮放的繪圖,一些繪圖操作在較高刻度值時顯着降低質量。這些操作實現爲以1.0比例繪製的紋理,由GPU轉換。在API級別<17中,使用這些操作會導致縮放工件隨着比例增加。

下表顯示何時更改實施以正確處理大比例:
圖片和圖形之硬件加速(7)

注:“簡單”形狀drawRect(), drawCircle(),drawOval(),drawRoundRect(),和 drawArc()(同useCenter = FALSE)與不具有PathEffect,並且不包含非默認連接(通過塗料發出的命令setStrokeJoin()/ setStrokeMiter())。在上面的圖表中,這些繪圖命令的其他實例屬於“複雜”。

查看圖層

在Android的所有版本中,視圖都可以通過使用視圖的繪圖緩存或通過使用來渲染到屏幕外緩衝區Canvas.saveLayer()。離屏緩衝區或圖層有幾種用途。動畫處理複雜視圖或應用合成效果時,您可以使用它們獲得更好的性能。例如,您可以使用Canvas.saveLayer()臨時渲染視圖到一個圖層並使用不透明度因子將其複合到屏幕上來實現淡入淡出效果。

從Android 3.0(API級別11)開始,您可以更好地控制如何以及何時在View.setLayerType()方法中使用圖層。該API採用兩個參數:要使用的圖層類型和Paint 描述應該如何合成圖層的可選對象。您可以使用該Paint參數將顏色過濾器,特殊混合模式或不透明度應用於圖層。視圖可以使用三種圖層類型之一:

  • LAYER_TYPE_NONE:視圖呈現正常,並且不被屏幕外緩衝區支持。這是默認行爲。
  • LAYER_TYPE_HARDWARE:如果應用程序是硬件加速的,則該視圖在硬件中呈現爲硬件紋理。如果應用程序不是硬件加速的,則此圖層類型的行爲與LAYER_TYPE_SOFTWARE。
  • LAYER_TYPE_SOFTWARE:視圖以軟件呈現爲位圖。

您使用的圖層類型取決於您的目標:

  • 性能:使用硬件圖層類型將視圖渲染爲硬件紋理。視圖渲染到圖層後,視圖調用之前不需要執行其繪圖代碼invalidate()。然後可以將一些動畫(如alpha動畫)直接應用到圖層上,這對於GPU來說非常有效。
  • 視覺效果:使用硬件或軟件圖層類型,並對Paint視圖應用特殊視覺處理。例如,您可以使用a繪製黑白視圖ColorMatrixColorFilter。
  • 兼容性:使用軟件層類型強制視圖以軟件呈現。如果硬件加速的視圖(例如,如果整個應用程序硬件加速),則會導致渲染問題,這是解決硬件渲染管道限制的簡單方法。

查看圖層和動畫

當您的應用程序硬件加速時,硬件層可以提供更快更流暢的動畫。在動畫處理髮生大量繪圖操作的複雜視圖時,以每秒60幀的速度運行動畫並不總是可行。這可以通過使用硬件層將視圖渲染爲硬件紋理來緩解。然後可以使用硬件紋理來爲視圖設置動畫,從而消除視圖在動畫時不斷重新繪製自身的需要。除非您更改視圖的屬性,調用invalidate()或invalidate()手動調用,否則不會重新繪製視圖。如果您在應用程序中運行動畫並且沒有獲得您想要的平滑結果,請考慮在動畫視圖上啓用硬件層。

當視圖由硬件層支持時,其某些屬性由圖層在屏幕上合成的方式處理。設置這些屬性將很有效,因爲它們不要求視圖失效並重繪。以下屬性列表會影響圖層合成的方式。爲這些屬性調用setter會導致最佳的失效並且不會重新繪製目標視圖:

  • alpha:更改圖層的不透明度
  • x,y,translationX,translationY:更改層的位置
  • scaleX,scaleY:更改圖層的大小
  • rotation,rotationX,rotationY:改變層的三維空間定位
  • pivotX,pivotY:更改圖層的轉換原點

這些屬性是使用動畫視圖時使用的名稱ObjectAnimator。如果你想訪問這些屬性,請調用適當的setter或getter。例如,要修改alpha屬性,請調用setAlpha()。以下代碼片斷展示了圍繞Y軸旋轉3D視圖的最有效方法:

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator.ofFloat(view, "rotationY", 180).start();

由於硬件層使用視頻內存,強烈建議您只在動畫期間啓用它們,然後在動畫完成後禁用它們。您可以使用動畫偵聽器完成此操作:

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
animator.start();

有關屬性動畫的更多信息,請參閱屬性動畫

技巧和竅門


切換到硬件加速的2D圖形可以立即提高性能,但您仍應該按照以下建議設計應用程序以有效使用GPU:

減少應用程序中的視圖數量

系統繪製的視圖越多,速度就越慢。這也適用於軟件渲染管道。減少視圖是優化UI的最簡單方法之一。

避免透支

不要在彼此頂部繪製太多的圖層。刪除任何完全被其他不透明視圖遮擋的視圖。如果您需要繪製混合在一起的多個圖層,請考慮將它們合併到一個圖層中。使用當前硬件的一個很好的經驗法則是每幀畫面的像素數不會超過2.5倍(位圖計數中的透明像素!)。

不要在繪製方法中創建渲染對象

一個常見的錯誤是每次創建一個新的Paint或新Path的渲染方法被調用。這迫使垃圾收集器更頻繁地運行,並繞過硬件管道中的緩存和優化。

不要經常修改形狀

實例中的複雜形狀,路徑和圓圈使用紋理蒙版進行渲染。每次創建或修改路徑時,硬件管道都會創建一個新的掩碼,這可能很昂貴。

不要經常修改位圖

每次更改位圖的內容時,下次繪製時都會再次將其作爲GPU紋理上傳。

小心使用阿爾法

當您使用setAlpha(),, AlphaAnimation或者使視圖變得半透明時ObjectAnimator,它將呈現在屏幕緩衝區中,該緩衝區將所需填充率加倍。在非常大的視圖上應用alpha時,請考慮將視圖的圖層類型設置爲 LAYER_TYPE_HARDWARE。

聯繫我

QQ:94297366
微信打賞:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公衆號推薦:

圖片和圖形之硬件加速(7)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章