国产午夜精品理论片,国产亚洲精品久久久999蜜臀,国产精品久久久久久久久免费,国产卡一卡二卡3卡4乱码,国产精品午夜无码av天美传媒

Android性能優(yōu)化之如何避免Overdraw
發(fā)布時間:2016/1/8 來源:搜數(shù)網(wǎng)絡(luò) 瀏覽:22

什么是Overdraw?

Overdraw就是過度繪制,是指在一幀的時間內(nèi)(16.67ms)像素被繪制了多次,理論上一個像素每次只繪制一次是最優(yōu)的,但是由于重疊的布局導(dǎo)致一些像素會被多次繪制,而每次繪制都會對應(yīng)到CPU的一組繪圖命令和GPU的一些操作,當(dāng)這個操作耗時超過16.67ms時,就會出現(xiàn)掉幀現(xiàn)象,也就是我們所說的卡頓,所以對重疊不可見元素的重復(fù)繪制會產(chǎn)生額外的開銷,需要盡量減少Overdraw的發(fā)生。
Android提供了測量Overdraw的選項(xiàng),在開發(fā)者選項(xiàng)-調(diào)試GPU過度繪制(Show GPU Overdraw),打開選項(xiàng)就可以看到當(dāng)前頁面Overdraw的狀態(tài),就可以觀察屏幕的繪制狀態(tài)。該工具會使用三種不同的顏色繪制屏幕,來指示overdraw發(fā)生在哪里以及程度如何,其中:
沒有顏色: 意味著沒有overdraw。像素只畫了一次。
藍(lán)色: 意味著overdraw 1倍。像素繪制了兩次。大片的藍(lán)色還是可以接受的(若整個窗口是藍(lán)色的,可以擺脫一層)。
綠色: 意味著overdraw 2倍。像素繪制了三次。中等大小的綠色區(qū)域是可以接受的但你應(yīng)該嘗試優(yōu)化、減少它們。
淺紅: 意味著overdraw 3倍。像素繪制了四次,小范圍可以接受。
暗紅: 意味著overdraw 4倍。像素繪制了五次或者更多。這是錯誤的,要修復(fù)它們。


552dd397d5e53.jpg


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

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

既然overdraw是因?yàn)橹貜?fù)繪制了同一片區(qū)域的像素點(diǎn),那我們首先想到的是解決布局問題。Android提供的Layout控件主要包括LinearLayout、TableLayout、FrameLayout、RelativeLayout。俗話說條條大路通羅馬,同一個界面我們可以使用不同的容器控件來表達(dá),但是各個容器控件描述界面的復(fù)雜度是不一樣的。一般來說LinearLayout最易,RelativeLayout較復(fù)雜。但是尺有所短,寸有所長,LinearLayout只能用來描述一個方向上連續(xù)排列的控件,而RelativeLayout幾乎可以用于描述任意復(fù)雜度的界面。但是我又要說但是了,表達(dá)能力越強(qiáng)的容器控件,性能往往略低一些,因?yàn)橄到y(tǒng)需要將更多的時間花在計(jì)算子控件的位置上。綜上所述:LinearLayout易用,效率高,表達(dá)能力有限。RelativeLayout復(fù)雜,表達(dá)能力強(qiáng),效率稍遜。
那么對于同一界面而言,作為開發(fā)者考慮是使用盡量少的、表達(dá)能力強(qiáng)的RelativeLayout作為容器,還是選擇多個、表達(dá)能力稍弱的LinearLayout來展示。從減少overdraw的角度來看,LinearLayout會增加控件數(shù)的層級,自然是RelativeLayout更優(yōu),但是當(dāng)某一界面在使用LinearLayout并不會比RelativeLayout帶來更多的控件數(shù)和控件層級時,LinearLayout則是首選。所以在表達(dá)界面的時候,作為一個有前瞻性的開發(fā)者要根據(jù)實(shí)際情況來選擇合適容器控件,在保證性能的同時,盡量避免overdraw。

第二招:去掉window的默認(rèn)背景

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

getWindow().setBackgroundDrawable(null);

或者在theme中添加

android:windowbackground="null"

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

有時候?yàn)榱朔奖銜冉oLayout設(shè)置一個整體的背景,再給子View設(shè)置背景,這里也會造成重疊,如果子View寬度mach_parent,可以看到完全覆蓋了Layout的一部分,這里就可以通過分別設(shè)置背景來減少重繪。再比如如果采用的是selector的背景,將normal狀態(tài)的color設(shè)置為“@android:color/transparent",也同樣可以解決問題。這里只簡單舉兩個例子,我們在開發(fā)過程中的一些習(xí)慣性思維定式會帶來不經(jīng)意的Overdraw,所以開發(fā)過程中我們?yōu)槟硞€View或者ViewGroup設(shè)置背景的時候,先思考下是否真的有必要,或者思考下這個背景能不能分段設(shè)置在子View上,而不是圖方便直接設(shè)置在根View上。

第四招:ClipRect & QuickReject

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

第五招:ViewStub

ViewStub是個什么東西?一句話總結(jié):高效占位符。
我們經(jīng)常會遇到這樣的情況,運(yùn)行時動態(tài)根據(jù)條件來決定顯示哪個View或布局。常用的做法是把View都寫在上面,先把它們的可見性都設(shè)為View.GONE,然后在代碼中動態(tài)的更改它的可見性。這樣的做法的優(yōu)點(diǎn)是邏輯簡單而且控制起來比較靈活。但是它的缺點(diǎn)就是,耗費(fèi)資源。雖然把View的初始可見View.GONE但是在Inflate布局的時候View仍然會被Inflate,也就是說仍然會創(chuàng)建對象,會被實(shí)例化,會被設(shè)置屬性。也就是說,會耗費(fèi)內(nèi)存等資源。
推薦的做法是使用android.view.ViewStub,ViewStub是一個輕量級的View,它一個看不見的,不占布局位置,占用資源非常小的控件??梢詾閂iewStub指定一個布局,在Inflate布局的時候,只有ViewStub會被初始化,然后當(dāng)ViewStub被設(shè)置為可見的時候,或是調(diào)用了ViewStub.inflate()的時候,ViewStub所向的布局就會被Inflate和實(shí)例化,然后ViewStub的布局屬性都會傳給它所指向的布局。這樣,就可以使用ViewStub來方便的在運(yùn)行時,要還是不要顯示某個布局。

<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" />

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

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

第六招:Merge

Merge標(biāo)簽有什么用呢?簡單粗暴點(diǎn)回答:干掉一個view層級。
Merge的作用很明顯,但是也有一些使用條件的限制。有兩種情況下我們可以使用Merge標(biāo)簽來做容器控件。第一種子視圖不需要指定任何針對父視圖的布局屬性,就是說父容器僅僅是個容器,子視圖只需要直接添加到父視圖上用于顯示就行。另外一種是假如需要在LinearLayout里面嵌入一個布局(或者視圖),而恰恰這個布局(或者視圖)的根節(jié)點(diǎn)也是LinearLayout,這樣就多了一層沒有用的嵌套,無疑這樣只會拖慢程序速度。而這個時候如果我們使用merge根標(biāo)簽就可以避免那樣的問題。另外Merge只能作為XML布局的根標(biāo)簽使用,當(dāng)Inflate以<merge />開頭的布局文件時,必須指定一個父ViewGroup,并且必須設(shè)定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標(biāo)簽使用" />
</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標(biāo)簽使用" />
</merge>

第七招:善用draw9patch

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

第八招:慎用Alpha

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

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

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

第九招:避免“OverDesign”

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


返回