1.1Controlling Hardware Acceleration
從Android3.0(API Level 11)開始,Android 2D渲染管道能夠更好的支持硬件加速。硬件加速執行的所有的繪圖操作都是使用GPU在View對象的畫布上來進行的。因為啟用硬件加速會增加資源的需求,因此這樣的應用會占用更多的內存。
硬件加速在target api大于等于14的情況下,是默認開啟的,但是我們也可以顯示的開啟硬件加速。如果應用程序只使用標準的View和Drawable,那么打開全局硬件加速不會導致任何不良的繪制影響。然而,由于硬件加速并不支持所有的2D圖形繪制操作,因此對于那些使用定制的View和繪制調用來說,打開全局硬件加速,會造成影響。對于這個問題,通常是對那些不可見的元素進行了異常或錯誤的像素渲染。為了避免這種問題,Android提供了多個級別的硬件加速操作(開啟或者關閉),具體可見控制硬件加速。
你可以控制硬件加速在下面不同的級別
(1).Application
(2).Activity
(2).Window
(2)View
Application level
在Android manifest文件,加入下面的屬性tag就可到以控制你的應用硬件加速。
<applicationandroid:hardwareAccelerated="true"...>
Activity level
啟用和不啟動硬件加速在你的activity級別,可以使用android:hardwareAccelerated屬性在元件中.下面的例子就是在application中使用硬件加速,而activity不使用。
<applicationandroid: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);
注意:當前情況,不能在View級別開啟硬件加速。
1.2判斷一個View對象是否被硬件加速
有些時候,知道當前的View對象(尤其是那些定制的View對象)是否被硬件加速對應用程序來說是非常有用的。如果應用程序做了很多定制的繪圖操作,并且不是所有的操作都會被新的渲染管道所支持,那么這種判斷就特別有用。
有兩種不同的方法來檢查應用程序是否被硬件加速了:
View.isHardwareAccelerated():返回值為true,View對象跟一個被硬件加速的窗口綁定;反之,未加速
Canvas.isHardwareAccelerated():返回值為true,Canvas對象被硬件加速;反之,未加速
1.3Android的繪圖模式(Android Drawing Models)
當硬件加速啟用時,Android框架會采用一個新的繪圖模式,這種模式利用顯示列表把應用程序渲染在屏幕上。充分理解顯示列表,以及它們是如何影響應用程序的,對于理解Android是如何繪制沒有硬件加速的View對象也是有益的。下面分別介紹基于軟件和硬件加速的繪圖模式。
當硬件加速啟用時,Android框架會采用一個新的繪圖模式,這種模式利用顯示列表把應用程序渲染在屏幕上。充分理解顯示列表,以及它們是如何影響應用程序的,對于理解Android是如何繪制沒有硬件加速的View對象也是有益的。下面分別介紹基于軟件和硬件加速的繪圖模式。
1.3.1基于軟件的繪圖模式(Software-based drawing model)
在軟件的繪圖模式中,View對象是通過以下兩個步驟來繪制的:
Invalidate the hierarchy
Draw the hierarchy
無論何時,當應用程序需要更新它的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()方法。
1.3.2硬件加速繪圖模式(Hardware accelerated drawing model)
這種模式下,Android系統依然會使用invalidate()和draw()來請求屏幕更新并且渲染View,但是實際的繪圖操作與基于軟件的繪圖模式是不同的。它會立即執行繪圖命令,Android系統把這些命令記錄在內部的顯示列表中,這個列表包含了View對象層次結構的繪圖代碼的輸出。另一個優化是:Android系統只需要針對由invalidate()方法調用所標記的View對象的臟區進行記錄和更新顯示列表。沒有失效的View對象能夠通過重新發布先前被記錄的顯示列表來進行簡單的重繪工作。這種新的繪圖模式包含三個階段:
Invalidate the hierarchy
Record and update display lists
Draw the display lists
使用這種模式,不能夠依賴相交的臟區的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)和它的父對象都會再次執行繪圖代碼。
Unsupported Drawing Operations
當硬件加速時,2D渲染管道支持最常用的Canvas繪圖操作以及許多較少使用的操作。 支持用于渲染隨Android提供的應用程序,默認小部件和布局以及常見的高級視覺效果(如反射和平鋪紋理)的所有繪圖操作。
下表描述了API級別各種操作的支持級別:
Canvas Scaling
首先構建了硬件加速2D渲染流水線,以支持非標尺繪圖,一些繪圖操作在較高的尺寸值下顯著降低了質量。 這些操作被實現為由GPU變換的規模1.0繪制的紋理。 在API級別<17中,使用這些操作將導致縮放工件隨規模而增加。
下表顯示了何時更改實施以正確處理大尺度:
注意:“簡單”形狀是帶有不具有PathEffect的Paint的drawRect(),drawCircle(),drawOval(),drawRoundRect()和drawArc()(with useCenter = false)), 包含非默認連接(通過setStrokeJoin()/ setStrokeMiter())。 這些繪制命令的其他實例位于上圖中的“復雜”下。
如果您的應用程序受到任何這些缺少的功能或限制的影響,您可以通過調用setLayerType(View.LAYER_TYPE_SOFTWARE,null)來關閉應用程序受影響部分的硬件加速。 這樣,您仍然可以利用其他地方的硬件加速。 有關如何在應用程序中啟用和禁用不同級別的硬件加速的更多信息,請參閱控制硬件加速。
1.4View 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對象(例如,如果整個應用程序都被硬件加速)發生渲染問題,那么使用軟件層類型來解決硬件渲染管道的限制是一個簡單的方法。
當應用程序被硬件加速的時候,硬件層能夠傳遞更快、更平滑的動畫。當播放具有復雜的繪圖操作的動畫時,以每秒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(newAnimatorListenerAdapter() {
? ?@Override
? ?publicvoidonAnimationEnd(Animator animation) {
? ? ? ? ? view.setLayerType(View.LAYER_TYPE_NONE,null);
? ? }
});
animator.start();
更多的屬性動畫的信息,請看Property Animation.
選擇硬件加速的2D圖形能夠有效改善性能,但是為了有效的使用GPU,應該按照以下建議設計應用程序:
系統繪制越多的View對象,就會越慢。這種情況也適用于軟件渲染管道。減少View對象的有效方法之一就是優化UI。
在彼此的頂部不要繪制太多的層。移除那些完全被別的不透明View遮蓋的View。對于當前硬件的一個好的原則是,每幀的像素數不要大于屏幕上像素數的2.5倍(以位圖的透明點陣數來計算)。
一個常見的錯誤是每次調用渲染方法時創建一個新的Paint對象或Path對象。這樣就會強制頻繁的運行垃圾回收,導致繞過硬件管道中的緩存和優化。
對于復雜的形狀,如路徑和圓,是使用紋理掩碼來呈現的。每次創建或修改路徑,硬件通道都要創建一個新的紋理遮罩,這樣會消耗大量的資源。
每次改變位圖內容,它都會被再次上傳到GPU的紋理,以供下次繪制。
當使用setAlpha()、 AlphaAnimation或ObjectAnimator,讓一個View對象半透明時,需要雙倍填充率來渲染到離屏緩存。當在一個大的View對象上應用透明效果時,要考慮把View對象的層類型設置為LAYER_TYPE_HARDWARE。
官方地址:http://developer.android.com/guide/topics/graphics/hardware-accel.html