轉:http://zuiniuwang.blog.51cto.com/3709988/721798android
從Android 3.0開始,Android 2D的繪製流程就設計爲可以更好地支持硬件加速。使用GPU的View在Canvas上進行畫的操做時都會使用硬件加速。在最新的Android版本里,圖形硬件加速及繪製技巧獲得了更好的提高,51CTO特約了最牛網站長做爲本站專欄做者,爲各位網友撰寫Android 4.0開發相關文章。web
1.Android 4.0硬件加速的使用緩存
1.1 硬件加速的控制級別框架
啓用硬件加速的最簡單方法就是爲整個系統打開硬件加速的全局設置。若是你的程序是標準View或者是Drawable 則硬件加速的全局設這並不會形成不良的影響。然而硬件加速並不支持全部2D畫的操做,因此開啓硬件加速可能會對使用自定義組件的應用程序形成影響,問題經常表如今不可見的元素異常和錯誤的像素渲染,爲了解決這個問題Android可讓你選擇啓動ide
1.1.1 Application級別函數
或者禁用如下級別的硬件加速:Application Activity Window 和 View 。性能
在你的Android Manifest文件中添加 屬性標記,以便爲整個應用程序使用硬件加速。優化
1.1.2 Activity級別動畫
若是你的應用程序不能在Application應用級別表現良好的話,則可使用對Activity進行單獨控制。要啓動或者禁用一個Activity的硬件加速,你可使用activity的android:hardwareAccelerated屬性。下面的一個列子使整個Application啓用硬件加速,可是對一個Activity禁止使用硬件加速。網站
1.1.3 Window級別
若是你須要更細粒度的控制,你能夠經過以下代碼給window進行加速。
- getWindow().setFlags(
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
注意:現階段你不能在Window級別對它禁用硬件加速。
1.1.4 View級別
咱們能夠對單獨的View在運行時階段禁用硬件加速。咱們可使用以下代碼:
- myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
注意:現階段不可以在View級別進行硬件加速。
1.2 判斷一個View是否已經啓用了硬件加速
有時候咱們須要知道一個應用程序是否已經啓用了硬件加速,特別是針對一些自定義控件。由於你的應用程序作了不少自定義「畫」的操做,但並非全部的過程都支持新的「畫」的渲染過程。
有兩種不一樣的方法來檢查Application是否啓用了硬件加速:
1.2.1 使用View.isHardwareAccelerated() 若是返回true則能夠說明這個View所在的窗口已經啓用了硬件加速。
1.2.2 Canvas.isHardwareAccelerated() 若是返回true則說明這個Canvas已經啓用了硬件加速。
若是你必需要在你的繪畫代碼中進行是否已經加速的檢查,若是可能的話請使用Canvas.isHardwareAccelerated()來代替View.isHardwareAccelerated()。當一個View是存在於一個已經加速的Windows上時,任然可使用沒有硬件加速的Canvas進行繪畫,這場發生在,好比,當咱們把一個View畫到Bitmap上而後用做緩存。
2.Android 4.0的繪製模型
當開啓了硬件加速,Android框架將會使用一種新的繪製模型,這種模型將會使用顯示列表把你的應用顯示到屏幕上。要徹底理解顯示列表和他們如何影響你的應用程序,理解Android 4.0如何在非硬件加速的狀況下如何繪製Views是頗有必要的,下面將分別介紹軟件加速和硬件加速。
2.1基於軟件的繪製模型
在基於軟件繪製模型中,View的繪製遵循如下兩步:1.使整個控件層級無效。2.對層級進行繪製。
當一個應用程序須要更新它UI的一部分時,它將會調用內容發生改變的View的invalidate()方法(或者invalidate的變體)。Invalidate的消息按照View的層級關係向上傳遞用以計算須要重畫的部分(即髒區域)。而後Android系統會對和髒區域有交集的全部View進行繪製,不幸的是這種模型中有兩個缺點:
2.1.1 在這種模型中當在不一樣的層進行畫的時候,會額外執行不少代碼。例如一個Button是位於另一個View之上,當對Button調用 Invalidate()時,Android就會對這個View進行重繪,即使這個View沒有發生任何變化。
2.1.2 第二個問題是這種繪製模型會隱藏你Application中的Bug。由於Android系統會對和髒區域有交集的View進行重繪,在這種狀況下若是一個view的內容發生了改變,即使這個View的Invalidate()的方法並無獲得調用,它也可能被重繪。你便會依賴調用了invalidate()的其餘的控件以便得到正確的行爲,所以每當你的Application發生改變時,這種行爲多要隨之發生改變。也是基於次因,在你的自定義控件中你必須不斷地調用invalidate()方法,當你的數據或者是狀態會影響View的繪製代碼時。
注意:Android的View當它們的屬性發生改變時會自動的調用Invalidate()。好比,你改變一個 Textview的背景或者是它的文本。
2.2 基於硬件加速模型
Android 系統仍然經過invalidate()和draw()去請求屏幕更新和從新渲染,可是實際處理畫的方式是不一樣的。不是當即執行畫的命令,Android而會將全部畫的命令記錄在一個顯示列表裏面,這個顯示列表包含了輸出的View層級的繪製代碼。還有一個優化就是Android在顯示列表中只會記錄和更新顯示層級中經過調用invalidate()函數被標記爲「髒」的view。沒有被請求刷新的view能夠經過從新請求先前的顯示列表以便重畫。新的繪製模型包括有三個步驟:1.禁用整個View層級。2.記錄和更新顯示列表。3.繪製顯示列表。
使用這個模型你不能依賴一個View和髒區域有交集就會執行draw()方法。要確保Android系統記錄了一個View的顯示列表,你必須調用invalidate()方法,若是忘記了調用刷新,會使View即使是發生了改變後也會看起來相同,這是一個比較容易發現bug的方式。
使用顯示列表的方式對動畫的表現也是頗有好處的,由於設置指定的屬性值,好比透明度或者旋轉,就不須要請求刷新目標View(這將自動執行)。這項優化也應用於有顯示列表的Views(啓用了硬件加速的View),例如,如今有一個LinearLayout包含了一個ListView和Button,listview在button的上面。這時候LinearLayout的顯示列表以下所示:
◆DrawDisplayList(ListView) ;
◆DrawDisplayList(Button) ;
假設你如今你想更新這個Listview的不透明度,在設置Listview的 setAlpha(0.5f) 屬性以後,LinearLayout的顯示列表應該包含以下:
◆ SaveLayerAlpha(0.5)
◆ DrawDisplayList(ListView)
◆ Restore
◆ DrawDisplayList(Button)
這時候繪製Listview的複雜過程就會省略了,取而代之的是簡單的更新了LinearLayout的顯示列表。若是一個應用程序並無啓用硬件加速,Listview和它的父view的畫的代碼都會從新執行。
3.Android 4.0 View的層
3.1層的分類
全部的Android版本都有能力對離屏緩衝進行渲染,或者是使用View的繪製緩衝,或者是使用Canvas.saveLayer()函數。離屏緩衝或者Layer可以有不少種應用,例如能使處理複雜view的動畫效果或者應用一些合成效果都有更好地表現。例如你能夠經過Canvas.saveLayer()的方式來對View作一個漸入漸出效果同時把它渲染到Layer中,而後再加上不透明效果合成後顯示到屏幕上。
由Android 3.0開始你就可以經過View.setLayerType()方法對什麼時候以及如何使用層有了更多的控制,這個API具備兩個參數一個是你想使用的層類型,另一個是可選參數Paint代表了Layer是如何被疊加的。你能夠把Paint參數應用到顏色過濾上,特別是混合模式或者是對一個layer進行不透明效果。一個View可使用以下的三種layer類型之一:
◆ LAYER_TYPE_NONE: 這個View將被按普通的方式進行渲染,可是不會返回一個離屏的緩衝,這個是默認的行爲。
◆ LAYER_TYPE_HARDWARE:若是這個應用被硬件加速的話,這個View將會在硬件中渲染爲硬件紋理,若是應用程序並無被硬件加速,則其效果和LAYER_TYPE_SOFTWARE是相同的。
◆ LAYER_TYPE_SOFTWARE: 此View 經過軟件渲染爲一個bitmap。
3.2 層的使用
使用層的類型取決於你的目的:
3.2.1 性能:使用硬件層來渲染一個View成爲硬件紋理。一旦一個View被渲染爲一個層,它的繪製代碼將不會獲得執行,直到你調用了invalidate()函數。對於一些動畫,好比透明動畫能夠直接應用到一個層上,這是GPU最有效率的使用方式。
3.2.2 顯示效果:使用硬件或者軟件層和Paint來對一個View進行特殊的視覺處理,例如你能夠對一個View經過使用ColorMatrixColorFilter來實現黑白效果。
3.2.3 兼容性:使用軟件層類型會強制使一個view在軟件中被渲染。若是一個view是硬件加速的話(好比你設置整個應用程序是硬件加速的話),同時有渲染的問題,這是一種很簡單的方式來限制硬件繪製流程。
3.3 View的層和動畫的關係
當你的應用程序已經使用了硬件加速的話,硬件層可以帶來更爲快速和更爲平滑的動畫效果。當對一個複雜的View進行動畫操做時,由於要進行不少的畫操做,因此並不可能老是能達到60幀每秒。在這種狀況下能夠經過硬件層來渲染爲硬件紋理來提升性能。硬件紋理操做能夠用做對一個view進行動畫操做,當進行動畫的時候能夠減小對View自身頻繁的重繪。除非你改變這個view的屬性(調用invalidate()方法)或者你手動的調用invalidate()。若是在你的應用中運行一個動畫,可是並無獲得你想要的平滑效果,能夠考慮爲你要動畫的view開啓硬件層。
當一個View經過硬件層返回時,當全部的層疊加後最終的畫面顯示在屏幕時,View一些屬性會被同時被處理。設置這些屬性是十分有效率的,由於他們不須要View去invalidate和重繪。以下的屬性將影響層的疊加,設置這些屬性將會使View自動請求刷新,並且不須要對View進行重繪。
◆alpha: 改變層的透明度。
◆x, y, translationX, translationY: 改變層的位置
◆scaleX, scaleY: 改變層的大小
◆rotation, rotationX, rotationY:在3D空間內改變層的方向
◆pivotX, pivotY: 指定它進行變形的原點位置
這些屬性是經過ObjectAnimator對象對一個view進行動畫操做時所使用的,若是你想訪問這些屬性,直接調用這些屬性的setter或者getter方法,例如想改變View的alpha則直接調用setAlpha()。以下的代碼片斷顯示了一個View經過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();
4 Android 4.0提示和技巧
切換到硬件加速2D圖形能夠當即加強表現,可是你仍是須要經過以下的建議來設計你的應用程序來更有效率的使用GPU。
4.1 減小你程序中使用View的數量
你係統中畫的view的數量越多,你的程序就會越慢,在軟件繪製的流程也是同樣的,減小view的數量是優化你UI的一個最簡單的方法。
4.2 避免過多繪製
不要過多的疊加層,當一個View被其餘view徹底遮擋住了的話,最好把被遮擋的view移除掉。若是你須要繪製不一樣的層作一個疊加效果的話,考慮把這些層合併爲一個層。就如今的硬件來看,有一個好的經驗就是動畫的每幀不要繪製多餘屏幕像素2.5倍的像素數量(bimap中的透明像素也計算在內)。
4.3 不要在繪製的方法中建立繪製對象
一個常見的錯誤就是當繪製方法被調用的時候,每次都要建立一個新的Paint或者Path。這將迫使垃圾回收器過於頻繁的運行,這將對緩衝和硬件的繪製形成影響。
4.4 不要過於頻繁的修改形狀
以複雜的shapes,path和旋轉爲例,這些繪製都會用到紋理的遮罩。每當你建立或者修改一個path,硬件渲染過程都會建立一個新的遮罩,這耗費的代價是至關大的。
4.5 不要過於頻繁的修改bitmap
每當修改一次bitmap的內容,當你下次再繪製它的時候都會以GPU的紋理形式上傳一次。
4.6 要當心使用alpha通道
當你使用setAlpha ,AlphaAnimation或者ObjectAnimator設置一個View的透明效果時。它將須要2倍離屏的渲染緩衝填充率,當應用一個alpha到一個大的View上的時候,考慮設置view 層的類型爲LAYER_TYPE_HARDWARE。