Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> android Android性能優化之如何避免Overdraw,androidoverdraw

android Android性能優化之如何避免Overdraw,androidoverdraw

編輯:關於android開發

android Android性能優化之如何避免Overdraw,androidoverdraw


什麼是Overdraw?

Overdraw就是過度繪制  

怎麼來消滅overdraw呢?總的原則就是:盡量避免重疊不可見元素的繪制,基於這個原則,我們大概可以想出以下幾招:

第一招:合理選擇控件容器

既然overdraw是因為重復繪制了同一片區域的像素點,那我們首先想到的是解決布局問題。Android提供的Layout控件主要包括 LinearLayout、TableLayout、FrameLayout、RelativeLayout。俗話說條條大路通羅馬,同一個界面我們可以 使用不同的容器控件來表達,但是各個容器控件描述界面的復雜度是不一樣的。一般來說LinearLayout最易,RelativeLayout較復雜。 但是尺有所短,寸有所長,LinearLayout只能用來描述一個方向上連續排列的控件,而RelativeLayout幾乎可以用於描述任意復雜度的 界面。但是我又要說但是了,表達能力越強的容器控件,性能往往略低一些,因為系統需要將更多的時間花在計算子控件的位置上。綜上所述: LinearLayout易用,效率高,表達能力有限。RelativeLayout復雜,表達能力強,效率低。

那麼對於同一界面而言,作為開發者考慮是使用盡量少的、表達能力強的RelativeLayout作為容器,還是選擇多個、表達能力稍弱的 LinearLayout來展示。從減少overdraw的角度來看,LinearLayout會增加控件數的層級,自然是RelativeLayout 更優,但是當某一界面在使用LinearLayout並不會比RelativeLayout帶來更多的控件數和控件層級時,LinearLayout則是 首選。所以在表達界面的時候,作為一個有前瞻性的開發者要根據實際情況來選擇合適容器控件,在保證性能的同時,盡量避免overdraw。

第二招:去掉window的默認背景

當我們使用了Android自帶的一些主題時,window會被默認添加一個純色的背景,這個背景是被DecorView持有的。當我們的自定義布 局時又添加了一張背景圖或者設置背景色,那麼DecorView的background此時對我們來說是無用的,但是它會產生一次Overdraw,帶來 繪制性能損耗。去掉window的背景可以在onCreate()中setContentView()之後調用

getWindow().setBackgroundDrawable(null);

或者在theme中添加

android:windowbackground="null";

第三招:去掉其他不必要的背景

有時候為了方便會先給Layout設置一個整體的背景,再給子View設置背景,這裡也會造成重疊,如果子View寬度mach_parent,可 以看到完全覆蓋了Layout的一部分,這裡就可以通過分別設置背景來減少重繪。再比如如果采用的是selector的背景,將normal狀態的 color設置為“@android:color/transparent",也同樣可以解決問題。這裡只簡單舉兩個例子,我們在開發過程中的一些習慣性 思維定式會帶來不經意的Overdraw,所以開發過程中我們為某個View或者ViewGroup設置背景的時候,先思考下是否真的有必要,或者思考下 這個背景能不能分段設置在子View上,而不是圖方便直接設置在根View上。

第四招:ClipRect & QuickReject

為了解決Overdraw的問題,Android系統會通過避免繪制那些完全不可見的組件來盡量減少消耗。但是不幸的是,對於那些過於復雜的自定義 的View(通常重寫了onDraw方法),Android系統無法檢測在onDraw裡面具體會執行什麼操作,系統無法監控並自動優化,也就無法避免 Overdraw了。但是我們可以通過canvas.clipRect()來幫助系統識別那些可見的區域。這個方法可以指定一塊矩形區域,只有在這個區域 內才會被繪制,其他的區域會被忽視。這個API可以很好的幫助那些有多組重疊組件的自定義View來控制顯示的區域。同時clipRect方法還可以幫助 節約CPU與GPU資源,在clipRect區域之外的繪制指令都不會被執行,那些部分內容在矩形區域內的組件,仍然會得到繪制。除了clipRect方 法之外,我們還可以使用canvas.quickreject()來判斷是否沒和某個矩形相交,從而跳過那些非矩形區域內的繪制操作。

第五招:ViewStub

ViewStub是個什麼東西?一句話總結: 高效占位符。

我們經常會遇到這樣的情況,運行時動態根據條件來決定顯示哪個View或布局。常用的做法是把View都寫在上面,先把它們的可見性都設為 View.GONE,然後在代碼中動態的更改它的可見性。這樣的做法的優點是邏輯簡單而且控制起來比較靈活。但是它的缺點就是,耗費資源。雖然把View 的初始可見View.GONE但是在Inflate布局的時候View仍然會被Inflate,也就是說仍然會創建對象,會被實例化,會被設置屬性。也就 是說,會耗費內存等資源。

推薦的做法是使用android.view.ViewStub,ViewStub是一個輕量級的View,它一個看不見的,不占布局位置,占用資源 非常小的控件。可以為ViewStub指定一個布局,在Inflate布局的時候,只有ViewStub會被初始化,然後當ViewStub被設置為可見 的時候,或是調用了ViewStub.inflate()的時候,ViewStub所向的布局就會被Inflate和實例化,然後ViewStub的布局 屬性都會傳給它所指向的布局。這樣,就可以使用ViewStub來方便的在運行時,要還是不要顯示某個布局。

 
<ViewStub
    android:id="@+id/stub_view"
    android:inflatedId="@+id/panel_stub"
    android:layout="@layout/progress_overlay"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom"/>

 

 

當你想加載布局時,可以使用下面其中一種方法:

 
((ViewStub) findViewById(R.id.stub_view)).setVisibility(View.VISIBLE);
View importPanel = ((ViewStub) findViewById(R.id.stub_view)).inflate();

  


 

第六招:Merge

Merge標簽有什麼用呢?簡單粗暴點回答: 干掉一個view層級。

Merge的作用很明顯,但是也有一些使用條件的限制。有兩種情況下我們可以使用Merge標簽來做容器控件。第一種子視圖不需要指定任何針對父視 圖的布局屬性,就是說父容器僅僅是個容器,子視圖只需要直接添加到父視圖上用於顯示就行。另外一種是假如需要在LinearLayout裡面嵌入一個布局 (或者視圖),而恰恰這個布局(或者視圖)的根節點也是LinearLayout,這樣就多了一層沒有用的嵌套,無疑這樣只會拖慢程序速度。而這個時候如 果我們使用merge根標簽就可以避免那樣的問題。另外Merge只能作為XML布局的根標簽使用,當Inflate以<merge />開頭的布局文件時,必須指定一個父ViewGroup,並且必須設定attachToRoot為true。

舉個簡單的例子吧:

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content" 
android:layout_height="wrap_content"
android:text="merge標簽使用"/>
</RelativeLayout>

  

把上面這個XML加載到頁面中,布局層級是RelativeLayout-TextView。但是采用下面的方式,把RelativeLayout提換成merge,RelativeLayout這一層級就被干掉了。

 
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView 
android:layout_width="wrap_content"
android:layout_height="wrap_content"
 android:text="merge標簽使用"/>
</merge>

  

 

第七招:善用draw9patch

給ImageView加一個邊框,你肯定遇到過這種需求,通常在ImageView後面設置一張背景圖,露出邊框便完美解決問題,此時這個 ImageView,設置了兩層drawable,底下一層僅僅是為了作為圖片的邊框而已。但是兩層drawable的重疊區域去繪制了兩次,導致 overdraw。優化方案: 將背景drawable制作成draw9patch,並且將和前景重疊的部分設置為透明。由於Android的2D渲染器會優化draw9patch中的 透明區域,從而優化了這次overdraw。 但是背景圖片必須制作成draw9patch才行,因為Android 2D渲染器只對draw9patch有這個優化,否則,一張普通的Png,就算你把中間的部分設置成透明,也不會減少這次overdraw。

第八招:慎用Alpha

假如對一個View做Alpha轉化,需要先將View繪制出來,然後做Alpha轉化,最後將轉換後的效果繪制在界面上。通俗點說,做Alpha 轉化就需要對當前View繪制兩遍,可想而知,繪制效率會大打折扣,耗時會翻倍,所以Alpha還是慎用。如果一定做Alpha轉化的話,可以采用緩存的 方式。

 
view.setLayerType(LAYER_TYPE_HARDWARE);
doSmoeThing();
view.setLayerType(LAYER_TYPE_NONE);

  

通過setLayerType方式可以將當前界面緩存在GPU中,這樣不需要每次繪制原始界面,但是GPU內存是相當寶貴的,所以用完要馬上釋放掉。

第九招:避免“OverDesign”

overdraw會給APP帶來不好的體驗,overdraw產生的原因無外乎:復雜的Layout層級,重疊的View,重疊的背景這幾種。開發 人員無節制的View堆砌,究其根本無非是產品無節制的需求設計。有道是“由儉入奢易,由奢入儉難",很多APP披著過度設計的華麗外衣,卻忘了簡單易用 才是王道的本質,紛繁復雜的設計並不會給用戶帶來好的體驗,反而會讓用戶有壓迫感,產品本身也有可能因此變得卡頓。當然,一切拋開業務談優化都是空中樓 閣,這就需要產品設計也要有一個權衡,在復雜的業務邏輯與簡單易用的界面展現中做一個平衡,而不是一味的OverDesign。 

http://www.bkjia.com/Androidjc/1192070.htmlwww.bkjia.comtruehttp://www.bkjia.com/Androidjc/1192070.htmlTechArticleandroid Android性能優化之如何避免Overdraw,androidoverdraw 什麼是Overdraw? Overdraw就是過度繪制 怎麼來消滅overdraw呢?總的原則就是:盡量避免重...

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