在日常開發(fā)中,我們管理View對象層次結構的方式可能會對的應用程序性能產生重大影響娜扇。 本文將介紹如何評估
軟件視圖層次結構是否會減慢你的應用程序的運行速度
越妈,并且提供了解決可能出現(xiàn)的問題的一些策略季俩。
布局和衡量績效
渲染管線包括布局和度量階段,在此階段系統(tǒng)將相關項目適當?shù)胤胖迷谀愕囊晥D層次結構中梅掠。這個階段的度量部分決定了View對象的大小和邊界酌住。布局部分確定在屏幕上放置View對象的位置。
這兩個流水線階段都會導致他們處理的每個視圖或布局的內存耗費成本很低阎抒。大多數(shù)情況下酪我,這個成本是最小的,不會顯著影響性能且叁。但是都哭,當應用程序添加或刪除View對象時,可能會更大逞带,例如RecyclerView
對象回收它們或重新使用它們质涛。如果View對象需要考慮調整大小來約束它的約束,成本也可以更高:例如掰担,如果你的應用程序調用了包裝文本SetText()
的 View對象汇陆,則 View可能需要調整大小。
如果這樣的情況花費太長時間带饱,則可導致幀的系統(tǒng)超出允許的16ms內渲染毡代,從而丟棄幀阅羹,并且動畫變得非常復雜。
因為你無法將這些操作移動到工作線程 - 你的應用程序必須在主線程上處理這些操作 - 所以最好的方法是優(yōu)化這些操作教寂,以便盡可能縮短時間捏鱼。
管理復雜性:布局很重要
Android 布局 允許你在視圖層次結構中嵌套UI對象。這種嵌套也可能會增加布局成本酪耕。當你的應用程序處理一個布局對象時导梆,應用程序也會在布局的所有子節(jié)點上執(zhí)行相同的過程。對于復雜的布局迂烁,有時僅在系統(tǒng)首次計算布局時才會產生成本看尼。例如,當你的應用程序在RecyclerView對象中回收復雜的列表項時 盟步,系統(tǒng)需要布置所有對象藏斩。在另一個例子中,微不足道的變化可以傳播到父級却盘,直到他們到達一個不影響父級大小變化的對象狰域。
布局花費特別長時間的最常見情況是View對象的層次結構彼此嵌套。每個嵌套的布局對象都會增加布局階段的成本黄橘。層次越平坦兆览,布局階段完成所需的時間越少。
Double Taxation
通常塞关,框架一次執(zhí)行布局 或度量階段抬探,并且很快。但是描孟,在一些更復雜的布局情況下驶睦,框架可能需要多次遍歷層次結構中需要多次傳遞才能最終定位元素的部分砰左。不得不執(zhí)行多個布局和度量迭代被稱為double taxation匿醒。
例如,當你使用RelativeLayout允許你的View根據(jù)其他View對象的位置來定位對象的容器時缠导,框架將執(zhí)行以下操作:
- 執(zhí)行布局和度量傳遞廉羔,在此期間,框架根據(jù)每個child的請求計算每個子對象的位置和大小僻造。
- 使用這些數(shù)據(jù)憋他,同時考慮對象權重,找出相關視圖的正確位置髓削。
- 執(zhí)行第二個布局傳遞以完成對象的位置竹挡。
- 進入渲染過程的下一個階段。
視圖層次越多立膛,潛在的性能損失就越大揪罕。除了RelativeLayout
可能會導致Double Taxation
的Containers以外梯码。例如:
- 如果將
LinearLayout
視圖設置為水平,視圖可能會導致雙重布局和度量傳遞好啰。如果添加measureWithLargestChild
轩娶,則在垂直方向上也可能發(fā)生雙重布局和度量傳遞,在這種情況下框往,框架可能需要執(zhí)行第二個傳遞來解析正確大小的對象鳄抒。
該GridLayout
有一個類似的問題。雖然這個容器也允許相對定位椰弊,但是通常通過預處理子視圖之間的位置關系來避免Double Taxation许溅。但是,如果布局使用權重或填充 Gravity類男应,那么預處理的好處將會丟失闹司,如果容器是一個RelativeLayout
,框架可能必須執(zhí)行多個過程沐飘。 - 多次布局和測量通行證本身不是一個性能負擔游桩。但如果他們在錯誤的地方,他們就會變成這樣耐朴。你應該警惕以下情況之一適用于你的容器:
- 它是視圖層次結構中的根元素借卧。
- 它下面有一個深層次的視圖層次結構。
- 它有很多填充屏幕的實例筛峭,類似于ListView對象中的child铐刘。
診斷視圖層次結構問題
布局性能是一個復雜的問題,有許多方面影晓。有幾個工具可以為你提供有關性能問題發(fā)生位置的明確信息镰吵。其他一些工具提供的確切信息較少,但也可以提供有用的提示挂签。
Systrace
提供有關性能的優(yōu)秀數(shù)據(jù)的一個工具是內置于Android SDK中的Systrace疤祭。Systrace工具允許您收集和檢查整個Android設備的計時信息,讓您了解布局性能問題何時會導致性能問題饵婆。
Profile GPU rendering
最有可能為你提供有關性能瓶頸的具體信息的另一個工具是設備上的 配置文件GPU渲染工具勺馆,可以在由Android 6.0(API級別23)
和更高版本支持的設備上使用。此工具允許您查看每個渲染幀的布局和度量階段所花費的時間侨核。這些數(shù)據(jù)可以幫助您診斷運行時性能問題草穆,并幫助您確定需要解決的布局和度量問題。
在其捕獲的數(shù)據(jù)的圖形表示中搓译,配置文件GPU渲染使用藍色來表示布局時間悲柱。
Lint
Android Studio的Lint
工具可以幫助您獲得視圖層次結構中效率低下的感覺。要使用此工具些己,請選擇Analyze> Inspect Code
豌鸡,如圖1所示跑芳。
有關各種布局項目的信息顯示在`` Android> Lint> Performance```下。要查看更多詳細信息直颅,可以單擊每個項目將其展開博个,并在屏幕右側的窗格中查看更多信息。圖2顯示了這種顯示的一個例子功偿。
點擊其中一個項目盆佣,在右側窗格中顯示與該項目相關的問題。
Layout Inspector
Android Studio
的Layout Inspector
工具提供了應用程序視圖層次結構的可視化表示械荷。這是瀏覽應用程序層次結構的好方法共耍,讓特定視圖的父鏈的清晰可視化,并允許你檢查應用程序構造的布局吨瞎。Layout Inspector
提出的觀點也可以幫助識別雙重征稅帶來的性能問題痹兜。它還可以為你提供一個簡單的方法來識別嵌套布局的深度鏈,或嵌套child的大量布局區(qū)域颤诀,這是潛在的性能耗費問題的根原字旭。在這些情況下,布局和度量階段可能會耗費很多內存崖叫,導致性能問題遗淳。
解決視圖層次問題
解決視圖層次結構中出現(xiàn)的性能問題背后的基本概念比較簡單,但是在實踐中確實不太容易心傀。防止視圖層級施加性能懲罰包括flattening視圖層次結構和減少double taxation的雙重目標
刪除多余的嵌套布局
開發(fā)人員經常使用比所需更多的嵌套布局屈暗。例如, RelativeLayout
容器可能包含也是RelativeLayout
容器的單個子布局 脂男。這種嵌套意味著冗余养叛,并為視圖層次結構增加了不必要的耗費。
Lint可以經常為你解決這個問題宰翅,減少調試時間弃甥。
使用Merge/Include
冗余嵌套布局的一常見原因是<include>
標簽。例如堕油,你可以定義一個可重用的布局潘飘,如下所示:
<LinearLayout>
<!-- some stuff here -->
</LinearLayout>
</pre>
然后用一個include
標簽將這個項目添加到父容器中:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/app_bg"
android:gravity="center_horizontal">
<include layout="@layout/titlebar"/>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
android:padding="10dp" />
...
</LinearLayout>
其中肮之,并沒什么必要將第一布局嵌套在第二布局內掉缺。merge
標簽可以幫助防止此問題。
采用更輕量的布局
你可能無法調整現(xiàn)有的布局方案戈擒,使其不包含多余的布局眶明。在某些情況下,唯一的解決方案可能是通過切換到完全不同的布局類型來使你的層次結構變平筐高。
例如搜囱,你可能會發(fā)現(xiàn)一個TableLayout
提供了與具有多個位置依賴關系的更復雜布局相同的功能丑瞧。在Android的N版本中, ConstraintLayout
類提供了類似的功能RelativeLayout
蜀肘,但成本消耗卻相當?shù)汀?/p>
原文鏈接:https://developer.android.google.cn/topic/performance/launch-time.html?hl=zh-cn