Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android app性能優化解決卡慢頓之布局優化

Android app性能優化解決卡慢頓之布局優化

編輯:關於Android編程

前面博客分析了導致app卡頓慢的直接原因,這裡就從原因出發,分析一些優化方案(這裡主要是從直接影響渲染機制的布局相關進行分析)

1)Invalidations,Layouts,andPerformance(動畫,布局的優化)

順滑精妙的動畫是app設計裡面最重要的元素之一,這些動畫能夠顯著提升用戶體驗。下面會講解Android系統是如何處理UI組件的更新操作的。

通常來說,Android需要把XML布局文件轉換成GPU能夠識別並繪制的對象。這個操作是在DisplayList的幫助下完成的。DisplayList持有所有將要交給GPU繪制到屏幕上的數據信息。

在某個View第一次需要被渲染時,DisplayList會因此而被創建,當這個View要顯示到屏幕上時,我們會執行GPU的繪制指令來進行渲染。如果你在後續有執行類似移動這個View的位置等操作而需要再次渲染這個View時,我們就僅僅需要額外操作一次渲染指令就夠了。然而如果你修改了View中的某些可見組件,那麼之前的DisplayList就無法繼續使用了,我們需要回頭重新創建一個DisplayList並且重新執行渲染指令並更新到屏幕上。

需要注意的是:任何時候View中的繪制內容發生變化時,都會重新執行創建DisplayList,渲染DisplayList,更新到屏幕上等一系列操作。這個流程的表現性能取決於你的View的復雜程度,View的狀態變化以及渲染管道的執行性能。舉個例子,假設某個Button的大小需要增大到目前的兩倍,在增大Button大小之前,需要通過父View重新計算並擺放其他子View的位置。修改View的大小會觸發整個HierarcyView的重新計算大小的操作(特別是繪制動畫時影響是很嚴重的)。如果是修改View的位置則會觸發HierarchView重新計算其他View的位置。如果布局很復雜,這就會很容易導致嚴重的性能問題。我們需要盡量減少Overdraw。

\

我們可以通過前面介紹的MonitorGPURendering來查看渲染的表現性能如何,另外也可以通過開發者選項裡面的ShowGPUviewupdates來查看視圖更新的操作,最後我們還可以通過HierarchyViewer這個工具來查看布局,使得布局盡量扁平化(層數盡量減少),移除非必需的UI組件,這些操作能夠減少Measure,Layout的計算時間,制作動畫時,運動元素與其父控件的關系要好好考慮,盡量在運動元素運動過程中,不會導致其父控件變化,否則就會導致父控件重繪,整個重繪可能影響到整個布局文件,這就是你做出的動畫卡頓慢的可能原因之一。

2)Overdraw,Cliprect,QuickReject(過度繪制)

引起性能問題的一個很重要的方面是因為過多復雜的繪制操作。我們可以通過工具來檢測並修復標准UI組件的Overdraw問題,但是針對高度自定義的UI組件則顯得有些力不從心。

有一個竅門是我們可以通過執行幾個APIs方法來顯著提升繪制操作的性能。前面有提到過,非可見的UI組件進行繪制更新會導致Overdraw。例如NavDrawer從前置可見的Activity滑出之後,如果還繼續繪制那些在NavDrawer裡面不可見的UI組件,這就導致了Overdraw。為了解決這個問題,Android系統會通過避免繪制那些完全不可見的組件來盡量減少Overdraw。那些NavDrawer裡面不可見的View就不會被執行浪費資源(對於不需要用戶看見的元素,設置屬性為gone,這可以在一定程度上優化性能,這時與設置成invisable相比較的)。

\

但是不幸的是,對於那些過於復雜的自定義的View(重寫了onDraw方法),Android系統無法檢測具體在onDraw裡面會執行什麼操作,系統無法監控並自動優化,也就無法避免Overdraw了。但是我們可以通過canvas.clipRect()來幫助系統識別那些可見的區域。這個方法可以指定一塊矩形區域,只有在這個區域內才會被繪制,其他的區域會被忽視。這個API可以很好的幫助那些有多組重疊組件的自定義View來控制顯示的區域。同時clipRect方法還可以幫助節約CPU與GPU資源,在clipRect區域之外的繪制指令都不會被執行,那些部分內容在矩形區域內的組件,仍然會得到繪制。

 

\

 

除了clipRect方法之外,我們還可以使用canvas.quickreject()來判斷是否沒和某個矩形相交,從而跳過那些非矩形區域內的繪制操作。做了那些優化之後,我們可以通過上面介紹的ShowGPUOverdraw來查看效果。

 

 

3)CustomViewsandPerformance(自定義視圖的優化)

Android系統有提供超過70多種標准的View,例如TextView,ImageView,Button等等。在某些時候,這些標准的View無法滿足我們的需要,那麼就需要我們自己來實現一個View,這節會介紹如何優化自定義View的性能。

通常來說,針對自定義View,我們可能犯下面三個錯誤:

·UselesscallstoonDraw():我們知道調用View.invalidate()會觸發View的重繪,有兩個原則需要遵守,第1個是僅僅在View的內容發生改變的時候才去觸發invalidate方法,第2個是盡量使用ClipRect等方法來提高繪制的性能。

·Uselesspixels:減少繪制時不必要的繪制元素,對於那些不可見的元素,我們需要盡量避免重繪。

·WastedCPUcycles:對於不在屏幕上的元素,可以使用Canvas.quickReject把他們給剔除,避免浪費CPU資源。另外盡量使用GPU來進行UI的渲染,這樣能夠極大的提高程序的整體表現性能。

最後請時刻牢記,盡量提高View的繪制性能,這樣才能保證界面的刷新幀率盡量的高。

 

4)HiddenCostofTransparency(透明效果的隱藏性能消耗)

這小節會介紹如何減少透明區域對性能的影響。通常來說,對於不透明的View,顯示它只需要渲染一次即可,可是如果這個View設置了alpha值,會至少需要渲染兩次。原因是包含alpha的view需要事先知道混合View的下一層元素是什麼,然後再結合上層的View進行Blend混色處理。

在某些情況下,一個包含alpha的View有可能會觸發改View在HierarchyView上的父View都被額外重繪一次。下面我們看一個例子,下圖演示的ListView中的圖片與二級標題都有設置透明度。

\

大多數情況下,屏幕上的元素都是由後向前進行渲染的。在上面的圖示中,會先渲染背景圖(藍,綠,紅),然後渲染人物頭像圖。如果後渲染的元素有設置alpha值,那麼這個元素就會和屏幕上已經渲染好的元素做blend處理。很多時候,我們會給整個View設置alpha的來達到fading的動畫效果,如果我們圖示中的ListView做alpha逐漸減小的處理,我們可以看到ListView上的TextView等等組件會逐漸融合到背景色上。但是在這個過程中,我們無法觀察到它其實已經觸發了額外的繪制任務,我們的目標是讓整個View逐漸透明,可是期間ListView在不停的做Blending的操作(由於listview的適配器中getview會頻繁調用,除了listview會這樣,還包括grideview也會),這樣會導致不少性能問題。

如何渲染才能夠得到我們想要的效果呢?我們可以先按照通常的方式把View上的元素按照從後到前的方式繪制出來,但是不直接顯示到屏幕上,而是使用GPU預處理之後,再又GPU渲染到屏幕上,GPU可以對界面上的原始數據直接做旋轉,設置透明度等等操作。使用GPU進行渲染,雖然第一次操作相比起直接繪制到屏幕上更加耗時,可是一旦原始紋理數據生成之後,接下去的操作就比較省時省力(其實是將cpu的事落到gpu上去做,並且由於gpu的紋理機制讓整體性能提升)。

\

如何才能夠讓GPU來渲染某個View呢?我們可以通過setLayerType的方法來指定View應該如何進行渲染,從SDK16開始,我們還可以使用ViewPropertyAnimator.alpha().withLayer()來指定。如下圖所示:

\

另外一個例子是包含陰影區域的View,這種類型的View並不會出現我們前面提到的問題,因為他們並不存在層疊的關系。

\

為了能夠讓渲染器知道這種情況,避免為這種View占用額外的GPU內存空間,我們可以做下面的設置。

\

通過上面的設置以後,性能可以得到顯著的提升,如下圖所示:

 

\

 

 

總結一下:直接和渲染相關的就是布局文件,為了達到程序渲染最優,有幾個原則1.布局層數盡量少(扁平化)。2,盡量避免過度渲染。3.盡量簡化布局,4,經常變化的view的布局深度盡量低(每一次變化都會涉及上層及上上層控件的變化)。這是直接和渲染機制相關的布局,渲染還提出了要求,每一個功能相對單一的模塊的執行時間盡量接近16ms(這時幀率為60fps),最多為32ms,若再多就會影響用戶體驗了,其實16ms是一幀不落的繪制了,從16ms到32ms這段時間丟幀了,但是沒影響用戶體驗,但是大於32ms就很影響了,卡頓慢就出現了。至於這麼縮短每個模塊的時間,這個就得具體模塊的來定了。這裡的16ms和32ms是主線程的時間,所以一般耗時較大(例如網絡請求,文件操作,以及數據庫操作這些典型功能就算再優化,也很難將時間壓縮到16ms),這種情況就可以使用額外的線程來處理這些任務欄了,有人可能就有疑問了,既然線程可以解決問題,那我就不用優化了,直接使用線程就好了,這種說法其實是有問題的,因為線程管理還是得占用cpu時間的,如果線程數很多,cpu管理的時間就會變多,值得注意的是渲染機制的第一步解析xml布局文件(測量繪制DisplayList)的工作還得cpu做呢,所以可能會出現一種情況就是把耗時任務都放在新線程裡了,但是依舊還是卡頓慢。所以一般是優化後距離16ms這個標准還是相差很遠的情況才使用新線程。

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