API Guide—Hardware Acceleration官方原文地址:
http://developer.android.com/guide/topics/graphics/hardware-accel.html
轉載請註明原創地址:
http://blog.csdn.net/cj1029/article/details/46274203
硬件加速
從Android 3.0的(API級別11)開始,Android的2D渲染管道支持硬件加速,這意味着所有View 的canvas的繪製都會使用GPU,這也會導致app應用將消耗更多的內存。
在API >= 14上,硬件加速是默認開啓的,如果app程序使用的都只是標準的View和Drawable,則全局都打開硬件加速,是一定不會有任何副作用的。但是,由於硬件加速不支持所有的2D繪圖操作,開啓硬件加速可能會影響自定義View或繪圖調用,常見的問題表現爲View元素不可見,異常或像素渲染錯誤等。爲了解決這個問題,Android允許開發者在多個層級上自行選擇啓用或禁用硬件加速。
硬件加速的級別
Application級別
在manifest文件<application>
tag下添加hardwareAccelerated屬性,控制整個應用程序是否開用硬件加速
<application android:hardwareAccelerated="true" ...>
Activity級別
如果app程序部分Activity啓用硬件加速會有副作用,則可以通過在manifest文件<activity>
tag下添加hardwareAccelerated屬性來關閉這些Activity的硬件加速
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
Window級別
如果需要更細粒度的控制,還可以通過以下代碼啓用一個窗口的硬件加速:
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
注意: 目前窗口級別不支持關閉硬件加速
View級別
更細粒度的,還可以在運行時關閉一個View的硬件加速,代碼如下:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
注意: 目前可以關閉View級別的硬件加速,但是不能在View級別開啓硬件加速,因爲還依賴其他的設置
確定View是否處於硬件加速狀態
有時是應用程序需要知道當前是否開啓了硬件加速,尤其是有自定義視圖的情況下,由於不是所有的自定義的繪製操作新的渲染管倒都支持,此時知道硬件加速是否開啓就變得尤爲有用。
目前有兩種不同的方式來檢查應用程序是否處於硬件加速狀態:
//returns true if the View is attached to a hardware accelerated window.
View.isHardwareAccelerated()
//returns true if the Canvas is hardware accelerated
Canvas.isHardwareAccelerated()
如果這個驗證是必須的,則建議在draw的函數中使用Canvas.isHardwareAccelerated()
,因爲如果一個View被attach到一個硬件加速的Window上,即使沒有硬件加速的Canvas,它也是可以被繪製的。比如:將一個View以bitmap的形式進行緩存
Android的繪製模型
當硬件加速開啓時,Android框架會採用了新的繪圖模型,通過顯示列表來把應用程序渲染到屏幕上。充分理解顯示列表以及它們如何影響應用程序,同樣有助於我們理解在沒有啓用硬件加速情況下Android是如何繪製View的。下面的詳細介紹基於軟件的繪圖模型和基於硬件加速的繪圖模型。
基於軟件的繪圖模型
在軟件的繪圖模型中,View繪製需要以下兩個步驟:
1. Invalidate the hierarchy
2. 繪製hierarchy
當一個view內容發生變化時會調用invalidate() 更新UI,失效(invalidation)消息將會在整個視圖層中傳遞,計算每個需要重繪的區域(即髒區域)。 然後Android系統將會重繪所有和髒區域有交集的view。不幸的是這個繪圖模型有兩個缺點:
- 每個繪製操作中會執行不必要的代碼(過度重繪)。例如應用程序調用invalidate()重繪button,而button又位於另一個view之上,即使該view沒有變化也會進行重繪
- 這個模型可能會掩蓋掉應用程序中的一些bug導致其被發現不了。因爲android系統會重繪與髒區域有交集的view,所以view的內容可能會在沒有調用invalidate()的情況下重繪。這可能會導致一個view只有在其它view時失效纔得到正確的行爲。這種行爲可能在每次修改應用的時候都會發生改變。因此,你需要一直調用invalidate在你的自定義控件上,不管那些涉及到重繪的代碼是否修改了數據或狀態
注意: android view 在屬性值變化的時候會自動調用invalidate,比如一個TextView的顏色或者背景的變化
基於硬件加速的繪圖模型
Android系統仍然使用invalidate()和draw()來更新UI繪製view,但在實際處理繪製上有所不同。
硬件加速下Android系統不會立即執行繪製命令,而是把它記錄到顯示列表,包含的視圖層次的繪圖輸出。另一個優化就是Android系統只需記錄和更新標記爲髒(通過invalidate()
)的view。還有沒有被invalidated的View可以簡單地通過重新發行之前記錄的顯示列表來重繪。新的繪製模型包含三個步驟:
1. Invalidate the hierarchy
2. 記錄和更新顯示列表
3. 繪製顯示列表
在這種模式下,你不能指望一個視圖相交的骯髒的地區會執行其draw()方法。爲了確保Android系統記錄視圖的顯示列表,你必須調用invalidate(),否則會導致View不會刷新。
使用顯示錶也有利於動畫性能,例如設置View的一些特殊的屬性alpha或旋轉,不需要invalidate目標View(它會自動完成)。這種優化也可以應用於所有顯示列表的視圖(當application硬件加速的時候)。舉個例子:假設有一個LinearLayout,包含一個ListView和一個Button,listview在上Button上方。那麼對於這個LinearLayout的顯示列表應該是這樣的:
DrawDisplayList(ListView)
DrawDisplayList(Button)
假設你想改變ListView的透明度,在listview上調用了setAlpha(0.5F)
之後,則顯示列表變爲:
SaveLayerAlpha(0.5)
DrawDisplayList(ListView)
Restore
DrawDisplayList(Button)
ListView複雜的繪製代碼並未執行。取而代之的是,系統只更新了簡單地更新了LinearLayout的顯示列表。如果沒有開啓硬件加速,則list及其父親的draw代碼塊都會被執行。
硬件加速不支持的繪圖操作
硬件加速支持大多數2D渲染管道的Canvas的繪製操作。
下圖描述了各種繪圖方法硬件加速開始支持的api level:
Canvas縮放
硬件加速的2D渲染管道的建立首先是爲了支持未縮放的繪製和在高縮放比會明顯降低繪製質量的繪製操作,這些繪製的操作由GPU轉化按照1.0的規模紋理實現,由GPU轉化但是過高的值會影響到繪圖操作的質量。
下圖顯示合適支持正確處理大縮放比的處理:
注意: ‘Simple’ shapes are drawRect()
, drawCircle()
, drawOval()
, drawRoundRect()
, and drawArc()
(with useCenter=false
) commands issued with a Paint that doesn’t have a PathEffect, and doesn’t contain non-default joins (via setStrokeJoin() / setStrokeMiter())
. Other instances of those draw commands fall under ‘Complex,’ in the above chart.
如果應用程序因爲這些特徵缺失或限制收到影響,你可以通過setLayerType(View.LAYER_TYPE_SOFTWARE, null)
關閉硬件加速,這不會影響到其他的地方使用硬件加速。
View Layer
在所有版本的Android,所有的View具有渲染到鎖屏緩衝區的能力,或通過View的繪製緩存,或者調用Canvas.saveLayer()
. 鎖屏緩衝區或者layers有幾種用途,可以用來實現複雜Views的動畫或者組合效果時提升動畫效果。比如, 實現漸變效果通過Canvas.saveLayer
渲染view到layer,
再通過與一個不透明的參數混合顯示到屏幕上。
在Android 3(API級別11開始),可以有更多的控制何時如何使用view的setlayertype()
方法。這個API有兩個參數:Layer的類型和一個可選的Paint,可以使用Paint參數應用到color filters,,特殊的混合模式,或者一個layer的不透明度。
View的layer類型有三種:
LAYER_TYPE_NONE
默認類型,view正常的渲染,不會通過鎖屏的緩衝區返回。LAYER_TYPE_HARDWARE
硬件加速開啓的情況下在硬件繪製模型中繪製成硬件紋理,若硬件加速未開啓則和LAYER_TYPE_SOFTWARE
一樣LAYER_TYPE_SOFTWARE
在軟件渲染模型中渲染成Bitmap
layer的類型取決於需求:
性能:
使用硬件層類型來渲染一個View到紋理層,一旦視圖被渲染成一層,其繪圖代碼不必執行,直到視圖調用invalidate()執行。一些動畫,如alpha動畫,然後可以被直接應用到層,使用GPU來做是非常高效的。視覺效應:
使用硬件或軟件層型和塗料適用於特殊的視覺處理的觀點。例如,你可以通過Colormatrixcolorfilter
畫一個黑白相間的視圖。兼容性:
使用一個軟件層類型去強制渲染一個視圖。如果一個被硬件加速的視圖(例如,如果你的整個應用程序的硬件加速),就會出現
渲染的問題,這是一個解決硬件渲染管線限制的簡單的方法。
View Layer與動畫
如果應用打開了硬件加速,硬件層可以提供更快和更平滑的動畫。在每秒60幀運行一個處理大量繪圖操作的複雜動畫有時候是不太可能的。這時可以通過使用硬件層將View渲染硬件紋理。硬件紋理可以用於動畫視圖,消除View在動畫時的不斷重繪。如此View不會重繪除非改變View的屬性,調用了invalidate()
,或者手動的調用invalidate()
,如果應用程序動畫的流暢度達不到預期,可以考慮使在動畫View上使用硬件圖層。
當一個視圖是由硬件層的支持,它的一些屬性通過layer被合成到屏幕。設置這些屬性將是非常高效的,因爲不需要View invalidate()
和重繪。下面列出的屬性會影響layer合成的方式。調用這些屬性的set方法是最佳的,不會造成view重繪。
- alpha —-> 改變透明度
- x, y, translationX, translationY —-> 改變layer位置
- scaleX, scaleY —-> 改變layer大小
- rotation, rotationX, rotationY —-> 三維空間中圖層的方向
- pivotX, pivotY —-> 改變layer的不動點
這些屬性動畫所使用的是ObjectAnimator
。如果要訪問這些屬性,調用相應的setter
或getter
即可。例如修改alpha屬性,調用setAlpha()
。下面的代碼片段顯示在繞Y軸旋轉的3D一viewiew最有效的方法:
view . setLayerType ( View . LAYER_TYPE_HARDWARE , null );
ObjectAnimator . ofFloat ( view , "rotationY" , 180 ). start ();
因爲硬件層消耗顯存,強烈建議只爲動畫期間開啓,動畫結束禁用。這一點可通過animation listeners
實現:
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
減少應用程序中的View的數量
需要繪製的視圖越多,繪製的速度就越慢,對於基於軟件的繪製模型也是一樣的。減少視圖是一個簡單的方法來優化UI。避免過度重繪
每個View的上面不要有太多的層次,移除View上面被其他View完全遮蓋的View。如果需要在每個view上繪製好幾層,請嘗試把它們合併爲一個單一的層。根據當前的硬件條件,一個較好的經驗準則是每幀像素繪製次數不超過屏幕上每幀的像素數2.5倍(透明像素按位圖計算)。【也是就說每個像素最多overdraw2.5次,關於overdraw】不要在draw方法中創建渲染對象
一個常見的錯誤是,每一次渲染方法被調用的時候都創建一個新的Paint
或一個新的Path
。這會迫使gc更加頻繁,也會繞過硬件管道的緩存和優化。不要太過頻繁的修改形狀
複雜的形狀如Path
,Circle
,每次都是通過紋理masks來渲染的,每次創建或者修改Path
,這個硬件管道會創建一個新的mask,這個代價是十分昂貴的。不要太過頻繁的修改Bitmap
每次改變Bitmap
的內容,在下次繪製時都會被以一個GPU紋理重新upload謹慎使用alpha
當使用setAlpha
,AlphaAnimation
或者ObjectAnimator
實現一個透明的View時,是會在一個雙倍填充率的離屏緩衝區渲染。當在大量view上做漸變時,建議考慮視圖的層類型設置爲 LAYER_TYPE_HARDWARE