標籤:錯誤 display row idg 可見 listener 原因 plain targe
做項目時,引導頁面的ViewPager報了OOM異常,圖片並不大,在資訊清單檔Application節點中添加了兩行代碼就解決了這個問題
android:hardwareAccelerated="false"
android:largeHeap="true"
從Android3.0(API Level 11)開始,Android 2D渲染管道能夠更好的支援硬體加速。硬體加速執行的所有的繪圖操作都是使用GPU在View對象的畫布上來進行的。因為啟用硬體加速會增加資源的需求,因此這樣的應用會佔用更多的記憶體。
啟用硬體加速的最容易的方法是給整個應用程式都開啟全域硬體加速功能。
如果應用程式只使用標準的View和Drawable,那麼開啟全域硬體加速不會導致任何的不良的繪製效果。但是,因為硬體加速並不支援所有的2D圖形繪製操作,所以對於那些使用定製的View和繪製調用的應用程式來說,開啟全域硬體加速,可以會影響繪製效果。問題通常會出現在對那些不可見的元素進行了異常或錯誤的像素渲染。為了避免這種問題,android提供以下層級,以便可選擇性的啟用或禁止硬體加速:
1. Application
2. Activity
3. Window
4. View
如果應用程式執行了定製化的繪圖,就要在實際的帶有硬體加速的硬體裝置上測試,以便發現問題。
控制硬體加速
能夠用以下層級來控制硬體加速 Application層級
在應用的Android資訊清單檔中,把下列屬性添加到<application>元素中,來開啟整個應用程式的硬體加速。
<application android:hardwareAccelerated="true" ...>
Activity層級
如果應用程式不能夠正確的使用被開啟的全域硬體加速,那麼也可以對Activity分別進行控制。在<activity>元素中使用android:hardwareAccelerated屬性,能夠啟用或禁止Activity層級的硬體加速。以下樣本啟用全域的硬體加速,但卻禁止了一個Activity的硬體加速: <application android:hardwareAccelerated="true"> <activity ... />
<activity android:hardwareAccelerated="false" /> </application>
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層除了禁止硬體加速以外,還有其他的功能,更多的相關資訊請看本文的“View層”。 判斷一個View對象是否被硬體加速
有些時候,尤其是對於那些定製的View對象,應用程式知道當前的View對象是否被硬體加速是十分有益的。如果應用程式做了很多定製的繪圖操作,並且不是所有的操作都會被新
的渲染管道所支援,那麼這種判斷就特別有用。
有兩種不同的方法來檢查應用程式是否被硬體加速了:
1. 如果一個View對象跟一個被硬體加速的視窗綁定,那麼View.isHardwareAccelerated() 方法就會返回true;
2. 如果一個Canvas對象被硬體加速了,那麼Canvas.isHardwareAccelerated()方法就會返回 true。
如果必須要在繪圖代碼中做這種檢查,那麼在可能的情況下,要使用Canvas.isHardwareAccelerated()方法來代替View.isHardwareAccelerated()方法。當一個View對象跟一個被硬體加速的視窗綁定的時候,它依然能夠使用使用一個非硬體加速的Canvas對象。例如,把一個View對象繪製到緩衝中的一個位元影像時就會發生這種情況。
HardAccelerated的作用:
當硬體加速被啟用時,Android架構會採用一個新的繪圖模式,這種模式利用顯示列表把應用程式呈現在螢幕上,這樣的速度更快。要充分理解顯示列表,以及它們是如何影響應用程式的,對於理解Android是如何繪製沒有硬體加速的View對象是有益的。下面分別介紹基於軟體的和硬體加速的繪圖模式。 基於軟體的繪圖模式
在軟體的繪圖模式中,View對象是通過以下兩個步驟來繪製的: 1. 讓View階層失效; 2. 繪製View階層。
無論何時,應用程式需要更新它的UI部分時,都回調用發生內容改變的View對象的invalidate()方法。無效的訊息請求會在View對象階層中傳遞,以便計算出需要重繪的螢幕地區(髒區)。然後,Android系統會在View階層中繪製所有的跟髒區相交的地區。不幸的是,這種方法有兩個缺點:
1. 這種模式在每個繪圖傳遞中需要很多的代碼執行。例如,如果應用程式調用了一個按鈕
的invalidate()方法,並且該按鈕位於另一個View對象的上方,那麼即使該View對象沒有變化,那麼Android系統也要重新繪製它。
2. 第二個問題是這個種繪圖模式能夠隱藏應用程式中的bug。因為當View對象跟髒區相
交時,Android系統就會重新繪製它,所以即使沒有調用View對象上的invalidate()方法,那麼View對象內容的改變也可能會導致它被重繪。當發生這種情況時,就要依賴另一個被失效的View對象來擷取正確的行為。這種行為能夠改變每次你對應用程式的修改。因為這個原因,在修改影響View對象的繪圖代碼的資料和狀態是,應該始終調用該定製View對象的invalidate()方法。
注意:在View對象的屬性發生變化時,如背景色或TextView對象中的文本等,Android系統會自動的調用該View對象的invalidate()方法。 硬體加速繪圖模式
這種模式下,Android系統依然會使用invalidate()方法和draw()方法來請求螢幕更新和展現View對象,但是實際的繪圖處理是不同的,它會立即執行繪圖命令,Android系統把這些命令記錄在內部的顯示列表中,列表中包含了View對象階層的繪圖代碼的輸出。另一種最佳化是:Android系統只需要針對由invalidate()方法調用所標記的View對象的髒區進行記錄和更新顯示列表。沒有失效的View對象能夠通過重新發布先前被記錄的顯示列表來進行簡單的重繪工作。這種繪圖模式包含三個階段: 1. 讓View的階層失效; 2. 記錄和更新顯示列表; 3. 繪製顯示列表。
使用這種模式,不能夠依賴相交的髒區View的draw()方法來執行繪圖工作。要確保Android系統記錄一個View對象的顯示列表,就必須調用invalidate()方法,如果忘記調用該方法,那麼在變化發生後,View對象看上去會跟變化之前相同,這是一個比較容易發現的Bug。 使用顯示列表對提升動畫的效能也是有好處的,因為設定諸如透明度、旋轉等屬性時,不需要讓目標View對象失效(系統會自動做這件事)。這種最佳化還適用於帶有顯示列表的View對象(應用程式被硬體加速時的任意View對象)。例如,假設有一個包含了一個Button對象的ListView對象的LinearLayout布局,那麼LinearLayout布局的顯示列表如下: 1. DrawDisplayList(ListView); 2. DrawDisplayList(Button)。
假設現在要改變ListView對象的不透明度,那麼在調用ListView對象的setAlpha(0.5f)方法時,顯示列表就包含了以下處理: 1. SaveLayerAlpha(0.5);
2. DrawDisplayList(ListView); 3. Restore;
4. DrawDisplayList(Button).
這裡沒有執行複雜的ListView對象的繪圖代碼。相反,系統只是比較簡單的更新了LinearLayout對象的顯示列表。在一個沒有啟用硬體加速的應用程式中,該列表(ListView)和它的父物件都會再次執行繪圖代碼。 不被硬體加速所支援的繪圖操作
在硬體加速的時候,2D渲染管道支援大多數的通常用於Canvas的繪圖操作,以及一些很少使用的操作。被用於渲染應用程式的所有的繪圖操作都有發送給Android系統,預設的Widget和布局,以及一些常用的可視效果,如反射和瓷磚的紋理效果都是被支援的。以下列出了已知的不支援硬體加速的操作: 1. Canvas
chipPath() chipRegion() drawPicture() drawPosText() drawTextOnPath() drawVertives() 2. Paint
setLinearText() setMaskFilter() setRasterizer()
另外,還有一些操作行為會因啟用了硬體加速而不同: 1. Canvas
clipRect():硬體加速會忽略XOR、Difference和ReverseDifference三種剪輯模式,3D變換不適用於剪輯矩形。
drawBitmapMesh():硬體加速會忽略顏色數組。 drawLines():硬體加速不支援消除鋸齒處理。
setDrawFilter():硬體加速能夠被設定,但是會被忽略。 2. Paint
setDither():硬體加速會忽略其設定。
setFilterBitmap():位元影像過濾是始終開啟的。
setShadowLayer():該項設定只對文本有效。 3. ComposeShader
ComposeShader對象只能包含不同類型的著色器(例如,BitmapShader和LinearGradient,但是不能夠包含兩個BitmapShader對象的執行個體)。
ComposeShader對象不能夠包含一個ComposeShader對象。
如果應用程式受到這些錯誤的功能或限制的影響,那麼能夠通過調用setLayerType(View.LAYER_TYPE_SOFTWARE, null)方法針對應用程式受到影響的部分來關閉硬體加速。這種方法,依然還能夠在其他的地方利用硬體加速。
View層
在Android的所有版本中,通過使用View對象的繪圖緩衝,或使用Canvas.saveLayer()方法,View對象都具有在螢幕外緩衝區呈現的能力。螢幕外緩衝區或層由多種用途。在呈現複雜的動畫或使用組合效果時,它們能夠獲得更好的效能。例如,使用Canvas.saveLayer()方法暫時把一個View對象呈現在一個層中,然後使用不透明因子把該View對象合成到螢幕上,從而實現淡入淡出的效果。
從Android3.0(API Level 11)開始,用View.setLayerType()方法使用層的方式和時機會更多的控制。這個API需要兩個參數:一個是層的類型,另一個是可選的,用於描述層應該如何被合成的Paint對象。能夠使用這個Paint對象來進行顏色過濾、特殊的混合模式、或者層的透明度。View對象能夠使用以下三種層類型:
LAYER_TYPE_NONE:View對象用普通的方式來呈現,並且不是由螢幕外緩衝來返回的。這種類型是預設的行為;
LAYER_TYPE_HARDWARE:如果應用程式是硬體加速的,那麼該View對象被呈現在硬體的一個硬體紋理中。如果沒有被硬體加速,那麼這種層類型的行為與LAYER_TYPE_SOFTWARE相同。
LAYER_TYPE_SOFTWARE:View對象會被呈現在軟體的一個位元影像中。 使用哪種層的類型,依賴以下目標:
1. 效能:使用硬體層類型,把View呈現到一個硬體紋理中。一旦該View對象被呈現到一
個層中,那麼它的繪圖代碼直到調用該View對象的invalidate()方法時才會被執行。對於某些動畫,如alpha動畫,就能夠直接使用該層,這麼做對於GPU來說是非常高效的。 2. 視覺效果:使用硬體或軟體層類型和一個Paint對象,能夠把一些特殊的視覺處理應用
給一個View對象。例如,使用ColorMatrixColorFilter對象繪製一個黑白相間的View對象。
3. 相容性:使用軟體層類型會強制把一個View對象呈現在軟體中。如果View對象被硬體
加速(例如,如果整個應用程式都被硬體加速)發生呈現問題,那麼使用軟體層類型來解決硬體呈現管道的限制是一個容易的方法。 View層和動畫
當應用程式被硬體加速的時候,硬體層能夠傳遞更快、更平滑的動畫。當播放具有複雜的繪圖操作的動畫時,以每秒60幀的速度播放不總是可能。這樣能夠通過使用硬體層把View對象呈現在硬體紋理中,可以緩解這種情況。硬體紋理能夠被用於動畫視圖,這樣在該View對象呈現動畫時,就可以消除View對象所需要的重繪操作。直到View對象的屬性發生變化時(invalidate()方法被調用),該View對象才會被重繪。如果在應用程式運行一個動畫,並且沒有獲得想要的平滑結果,就要考慮在動畫View上啟用硬體層。
當一個View被硬體層返回時,通過層方法處理的某些屬性會被合成到螢幕上。因為它們不
需要讓View對象失效和重繪,所以設定這些屬性是非常高效的。下面列出了影響層被合成的方式。調用這些屬性設定器,會導致失效處理的最佳化,並且不會對目標View對象進行重繪:
1. alpha:改變層的透明度;
2. x,y,translation,translation:改變層的位置; 3. scaleX,scaleY:改變成的尺寸;
4. rotation,rotation,rotationY:改變3D空間中層的方向; 5. 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(http://developer.android.com/guide/topics/graphics/prop-animation.html)
提示和技巧
切換到硬體加速的2D圖形能夠有效改善效能,但是依然要通過以下推薦的方式來設計應用程式,以便有效使用GPU:
1. 減少應用程式中View對象的數量
系統繪製越多的View對象,就會越慢。這種情況也適用於軟體呈現管道。減少View對象的有效方法之一就是最佳化UI。 2. 避免過度繪圖
在彼此的頂部不要繪製太多的層。因為刪除View時要同時刪除遮蓋在該View對象之上所有其他的不透明的View對象。如果需要繪製幾個圖層,要盡量在上面採用合成模式,考慮把它們合并成一個層。一個好的規則是:每幀的像素數不要大於螢幕上像素數的2.5倍(以位元影像的透明點陣數來計算)。 3. 不要在繪圖方法中建立呈現對象
一個常見的錯誤時每次調用呈現方法時建立一個新的Paint對象或Path對象。這樣就會強制頻繁的運行記憶體回收,並且會繞過硬體管道中的緩衝和最佳化。 4. 不要經常的編輯形狀
對於複雜的形狀,如路徑和圓,是使用紋理掩碼來呈現的。每次建立或修改路徑,硬體通道都要建立一個新的掩碼,這樣會消耗大量的資源。
查詢相關文檔得知是為了讓應用能申請使用更多的記憶體,我們知道安卓系統對於每個應用都有記憶體使用量的限制,機器的記憶體限制,在/system/build.prop檔案中配置的。
例如:
[java] view plain copy
- dalvik.vm.heapsize=128m
- dalvik.vm.heapgrowthlimit=64m
這裡,heapgrowthlimit就是一個普通應用的記憶體限制,用ActivityManager.getLargeMemoryClass()獲得的值就是這個。而heapsize是在manifest中設定了largeHeap=true之後,可以使用最大記憶體值。
設定largeHeap的確可以增加記憶體的申請量。但不是系統有多少記憶體就可以申請多少,而是由dalvik.vm.heapsize限制。
但是作為程式員的我們應該努力減少記憶體的使用,盡量想回收和複用的方法,而不是想方設法增大記憶體。當記憶體很大的時候,每次gc的時間也會長一些,效能會下降的。
Android--hardwareAccelerated 硬體加速詳解 android:largeHeap="true"