Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android系統教程 >> 安卓省電與加速 >> Android——硬件加速(Hardware Acceleration)

Android——硬件加速(Hardware Acceleration)

編輯:安卓省電與加速

從Android3.0(API Level 11)開始,Android 2D渲染管道能夠更好的支持硬件加速。硬件加速執行的所有的繪圖操作都是使用GPU在View對象的畫布上來進行的。因為啟用硬件加速會增加資源的需求,因此這樣的應用會占用更多的內存。

硬件加速在target api大於等於14的情況下,是默認開啟的,但是我們也可以顯示的開啟硬件加速。如果應用程序只使用標准的View和Drawable,那麼打開全局硬件加速不會導致任何不良的繪制影響。然而,由於硬件加速並不支持所有的2D圖形繪制操作,因此對於那些使用定制的View和繪制調用來說,打開全局硬件加速,會造成影響。對於這個問題,通常是對那些不可見的元素進行了異常或錯誤的像素渲染。為了避免這種問題,Android提供了多個級別的硬件加速操作(開啟或者關閉),具體可見控制硬件加速。

如果應用程序執行了定制化的繪圖,並且開啟了硬件加速,那麼就要在帶有硬件加速的真機上測試,以便發現問題。 不支持的繪制操作描述了硬件加速的問題,以及如何解決它們。

控制硬件加速(Controlling Hardware Acceleration)

android提供了以下四個個級別來控制硬件加速:
ApplicationActivityWindowView

Application級別

在應用的Android清單文件中,把下列屬性添加到元素中,能夠開啟整個應用程序的硬件加速:


Activity級別

如果應用程序不能正確的使用被打開的全局硬件加速,那麼也可以在Activity級別上進行控制。在元素中使用android:hardwareAccelerated屬性,能夠啟用或禁止Activity級別的硬件加速。以下示例啟用全局的硬件加速,但卻禁止了一個Activity的硬件加速:

    
    

Window級別

如果需要更精細的控制,就可以使用下列代碼來針對給定的Window來啟用硬件加速:

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

注意:當前情況,不能在Window級別禁止硬件加速。

View級別

在運行時,可以針對一個獨立的View對象使用下列代碼來禁止硬件加速:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

注意:當前情況,不能在View級別開啟硬件加速。View Layer除了禁止硬件加速以外,還有其他的功能,更多的相關信息請看本文的View layer。

判斷一個View對象是否被硬件加速

有些時候,知道當前的View對象(尤其是那些定制的View對象)是否被硬件加速對應用程序來說是非常有用的。如果應用程序做了很多定制的繪圖操作,並且不是所有的操作都會被新的渲染管道所支持,那麼這種判斷就特別有用。

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

View.isHardwareAccelerated():返回值為true,View對象跟一個被硬件加速的窗口綁定;反之,未加速Canvas.isHardwareAccelerated():返回值為true,Canvas對象被硬件加速;反之,未加速

Android的繪圖模式(Android Drawing Models)

當硬件加速啟用時,Android框架會采用一個新的繪圖模式,這種模式利用顯示列表把應用程序渲染在屏幕上。充分理解顯示列表,以及它們是如何影響應用程序的,對於理解Android是如何繪制沒有硬件加速的View對象也是有益的。下面分別介紹基於軟件和硬件加速的繪圖模式。

基於軟件的繪圖模式(Software-based drawing model)

在軟件的繪圖模式中,View對象是通過以下兩個步驟來繪制的:

讓View層次結構失效繪制View層次結構

無論何時,當應用程序需要更新它的UI部分時,它都會調用View#invalidate()(或者invalidate方法的相關變體)使UI內容改變。失效的消息請求會在View對象層次結構上進行一路傳遞,以便計算出需要重繪的屏幕區域(髒區)。然後,Android系統就會在View層次結構中繪制所有跟髒區相交的區域。不幸的是,這種繪圖模式有兩個缺點:

第一個問題,在每個繪圖傳遞中,這種繪圖模式都需要很多的代碼執行。例如,如果應用程序調用了一個按鈕的invalidate()方法,並且該按鈕位於另一個View對象的上方,那麼即使該View對象沒有變化,那麼Android系統也要重新繪制這個View對象。第二個問題,這個種繪圖模式能夠隱藏應用程序中的bug。由於Android系統會重新繪制其它跟髒區相交的View對象,所以即使沒有調用View對象上的invalidate()方法,那麼View對象內容的改變也可能會導致其它View被重繪。當發生這種情況時,就要依賴另一個被失效的View對象來獲取適當的行為。這種行為能夠改變每次你對應用程序的修改。因為這個原因,所以為了影響繪圖代碼,在修改定制View對象的數據和狀態時,應該始終調用該定制View對象的invalidate()方法。

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

硬件加速繪圖模式(Hardware accelerated drawing model)

這種模式下,Android系統依然會使用invalidate()和draw()來請求屏幕更新並且渲染View,但是實際的繪圖操作與基於軟件的繪圖模式是不同的。它會立即執行繪圖命令,Android系統把這些命令記錄在內部的顯示列表中,這個列表包含了View對象層次結構的繪圖代碼的輸出。另一個優化是:Android系統只需要針對由invalidate()方法調用所標記的View對象的髒區進行記錄和更新顯示列表。沒有失效的View對象能夠通過重新發布先前被記錄的顯示列表來進行簡單的重繪工作。這種新的繪圖模式包含三個階段:

讓View的層次結構失效記錄和更新顯示列表繪制顯示列表

使用這種模式,不能夠依賴相交的髒區的View#draw()執行。要確保Android系統記錄一個View對象的顯示列表,就必須調用invalidate()方法,如果忘記調用該方法,那麼在變化發生後,View對象看上去會跟變化之前相同。

使用顯示列表對提升動畫的性能也是有好處的,因為設置特殊屬性,諸如透明度、旋轉等屬性時,不需要請求目標View對象失效(系統會自動做這件事)。這種優化還適用於帶有顯示列表的View對象(應用程序被硬件加速時的任意View對象)。例如,假設有一個包含了一個Button對象的ListView對象的LinearLayout布局,那麼LinearLayout布局的顯示列表如下:

DrawDisplayList(ListView)DrawDisplayList(Button)

假設現在要改變ListView對象的透明度,那麼在調用ListView對象的setAlpha(0.5f)方法時,顯示列表就包含了以下處理:

SaveLayerAlpha(0.5);DrawDisplayList(ListView);Restore;DrawDisplayList(Button).

這裡沒有執行復雜的ListView對象的繪圖代碼。相反,系統只是比較簡單的更新了LinearLayout對象的顯示列表。在一個沒有啟用硬件加速的應用程序中,該列表(ListView)和它的父對象都會再次執行繪圖代碼。

不支持硬件加速的操作

在硬件加速的時候,2D渲染管道支持大多數的通常用於Canvas的繪圖操作,以及一些很少使用的操作。被用於渲染應用程序的所有的繪圖操作都有發送給Android系統,默認的Widget和布局,以及一些常用的可視效果,如反射和瓷磚的紋理效果都是被支持的。

下面給出一個鏈接——不同API級別上不支持的硬件加速的操作。

View Layers

在Android的所有版本中,通過使用View對象的繪圖緩沖,或使用Canvas.saveLayer()方法,View都具有渲染到離屏(off-screen)緩沖區的能力。離屏緩沖區或層有多種用途,在呈現復雜的動畫或使用組合效果時,能夠獲得更好的性能。例如,使用Canvas.saveLayer()可以實現淡入淡出的效果,先暫時把一個View對象渲染在一個層中,然後把它和不透明因子合成到屏幕上。

從Android3.0(API Level 11)開始,在如何和什麼時候使用View.setLayerType()問題上,Android提供了更多的控制。這個API攜帶兩個參數:一個是層的類型,另一個是可選的Paint對象,這個對象描述層應該如何被合成的。使用這個Paint對象能夠進行顏色過濾、特殊的混合模式、或者層的透明度。View對象能夠使用以下三種層類型:

LAYER_TYPE_NONE:View對象用普通的方式來渲染,並且不是由屏幕外緩存來返回的。這種類型是默認的行為。LAYER_TYPE_HARDWARE:如果應用程序是硬件加速的,那麼該View對象被渲染在硬件的一個硬件紋理(texture)中。如果沒有開啟硬件加速,那麼這種層類型的行為與LAYER_TYPE_SOFTWARE相同。LAYER_TYPE_SOFTWARE:View對象會被呈現在軟件的一個位圖中。

根據以下目的,選擇層類型:

性能:使用硬件層類型,把View渲染到一個硬件紋理中。一旦該View對象被渲染到一個層中,那麼它的繪圖代碼直到調用該View對象的invalidate()方法時才會被執行。對於某些動畫,如alpha動畫,就能夠直接使用該層,這麼做對於GPU來說是非常高效的。視覺效果:使用層類型(硬件或軟件)和一個Paint對象,能夠把一些特殊的視覺處理應用給一個View對象。例如,使用ColorMatrixColorFilter對象繪制一個黑白相間的View對象。兼容性:使用軟件層類型會強制把一個View對象渲染在軟件中。如果被硬件加速的View對象(例如,如果整個應用程序都被硬件加速)發生渲染問題,那麼使用軟件層類型來解決硬件渲染管道的限制是一個簡單的方法。

View layers and animations

當應用程序被硬件加速的時候,硬件層能夠傳遞更快、更平滑的動畫。當播放具有復雜的繪圖操作的動畫時,以每秒60幀的速度播放不總是可能的。但是,可以通過使用硬件層把View對象渲染在硬件紋理中,緩解這種情況。硬件紋理能夠被用於動畫視圖,這樣在該View對象呈現動畫時,就可以消除View對象所需要的重繪操作。除非該View對象的屬性發生變化時(invalidate()方法被調用),它才會被重繪。如果在應用程序運行一個動畫,並且沒有獲得想要的平滑結果,就要考慮在動畫View上啟用硬件層。

當一個View從硬件層被返回時,通過層方法處理的某些屬性會被合成到屏幕上。因為它們不需要讓View對象失效和重繪,所以設置這些屬性是非常高效的。下面列出了影響層被合成的方式。調用這些屬性設置器,會導致失效處理的優化,並且不會對目標View對象進行重繪:

alpha:改變層(layer)的透明度;x,y,translation,translation:改變層的位置;scaleX,scaleY:改變層的尺寸;rotation,rotation,rotationY:改變3D空間中層的方向;pivotX,pivotY:改變層的變換起源。

這些屬性是在用ObjectAnimator對象給View對象設置動畫時所使用的名稱。如果想要訪問這些屬性,就要調用相應的set或get方法。例如,要修改alpha屬性,就要調用setAlpha()方法。下面的代碼展示了在3D空間中圍繞Y軸旋轉View對象的最有效的方法:

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();

更多的屬性動畫的信息,請看Property Animation.

Tips and Tricks

選擇硬件加速的2D圖形能夠有效改善性能,但是為了有效的使用GPU,應該按照以下建議設計應用程序:

減少應用程序中View對象的數量

系統繪制越多的View對象,就會越慢。這種情況也適用於軟件渲染管道。減少View對象的有效方法之一就是優化UI。

避免過度繪圖

在彼此的頂部不要繪制太多的層。移除那些完全被別的不透明View遮蓋的View。對於當前硬件的一個好的原則是,每幀的像素數不要大於屏幕上像素數的2.5倍(以位圖的透明點陣數來計算)。

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

一個常見的錯誤是每次調用渲染方法時創建一個新的Paint對象或Path對象。這樣就會強制頻繁的運行垃圾回收,導致繞過硬件管道中的緩存和優化。

不要經常的編輯形狀

對於復雜的形狀,如路徑和圓,是使用紋理掩碼來呈現的。每次創建或修改路徑,硬件通道都要創建一個新的紋理遮罩,這樣會消耗大量的資源。

不要經常的編輯位圖

每次改變位圖內容,它都會被再次上傳到GPU的紋理,以供下次繪制。

要小心的使用alpha相關的方法

當使用setAlpha()、 AlphaAnimation或ObjectAnimator,讓一個View對象半透明時,需要雙倍填充率來渲染到離屏緩存。當在一個大的View對象上應用透明效果時,要考慮把View對象的層類型設置為LAYER_TYPE_HARDWARE。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved