View
物件階層的管理方式可能會對應用程式效能產生重大影響。本頁說明如何評估檢視區塊階層是否會導致應用程式速度變慢,並針對可能引起的問題提供解決策略。
本頁內容重點為改善以 View
為基礎的版面配置。如要進一步瞭解如何改善 Jetpack Compose 效能,請參閱「Jetpack Compose 效能」。
版面配置和測量效能
轉譯管道包含「版面配置與測量」階段,在這個階段,系統會在檢視區塊階層中適當定位相關項目。此階段的「測量」部分會決定 View
物件的尺寸和界線,「版面配置」部分會決定 View
物件在畫面上的位置。
這兩個管道階段都會耗用少量處理檢視區塊或版面配置的資源。在大多數情況下,這個資源用量很低,對效能沒有明顯影響。但是,如果應用程式新增或移除 View
物件 (例如 RecyclerView
物件回收或重複使用這些物件),影響就可能會較大。假如 View
物件需要根據限制調整大小,耗用的資源也可能比較多。舉例來說,如果應用程式對會將文字自動換行的 View
物件呼叫 SetText()
,View
就可能需要調整大小。
如果這類情況所需時間過長,影格就可能無法在 16 毫秒的允許時間內顯示,進而導致多個影格遭到捨棄,動畫也會出現卡頓。
由於無法將這些作業移至背景工作執行緒 (應用程式必須在主執行緒上進行處理),最好的解決方式是進行最佳化調整,盡可能縮短所需時間。
管理複雜的版面配置
您可以利用 Android Layouts,在檢視區塊階層中建立巢狀結構的 UI 物件。這個巢狀結構也可能會耗用版面配置的資源。應用程式處理版面配置的物件時,也會對該版面配置的所有子項執行相同程序。
如果是複雜的版面配置,有時只有在系統第一次計算版面配置時才會耗用資源。例如當應用程式回收 RecyclerView
物件中的某個複雜清單項目時,系統就需要安排所有物件的位置。在另一個範例中,細小的變更可能會向上傳播到鏈結中的父項,直到碰觸到不會影響父項尺寸的物件為止。
版面配置花費較長時間的常見原因,是多個階層的 View
物件構成彼此相互嵌套的巢狀結構。每個巢狀版面配置物件都會增加版面配置階段的耗用資源。階層越簡單,完成版面配置階段所需的時間就越短。
建議您使用版面配置編輯器建立 ConstraintLayout
,而非 RelativeLayout
或 LinearLayout
,因為這麼做通常更有效率,並且能減少版面配置的巢狀結構。不過,對於可以使用 FrameLayout
達成的簡易版面配置,則建議使用 FrameLayout
。
如果使用 RelativeLayout
類別,也許可以改為使用巢狀的非權重 LinearLayout
檢視區塊來達到相同效果,而且耗用資源較少。不過,如果您使用的是巢狀加權 LinearLayout
檢視區塊,則由於需要多項版面配置傳遞作業,所以版面配置耗用的資源會高很多,詳情請參閱下節。
建議您改用 RecyclerView
而非 ListView
,因為這麼做可以回收個別清單項目的版面配置,不僅效率更高,也能改善捲動效能。
重複作業
一般來說,架構會在單次傳遞中執行版面配置或測量階段。然而,在一些較複雜的版面配置中,在決定元素最後的位置前,架構可能必須針對需多次傳遞才能解析的階層部分,多次執行疊代。這種必須執行多項版面配置和測量疊代作業的情形,稱為「重複作業」。
舉例來說,使用 RelativeLayout
容器時,因為此容器可讓您依照其他 View
物件的位置,相對放置 View
物件,架構會執行以下動作:
- 執行版面配置和測量傳遞,架構會在此期間根據各子項的要求,計算每個子項物件的位置和尺寸。
- 使用這項資料,並將物件權重納入考量,算出相關檢視區塊的正確位置。
- 執行第二次版面配置傳遞,確定物件的位置。
- 繼續進行轉譯程序的下一階段。
檢視區塊階層的層級越多,對效能的負面影響就越大。
如先前所述,除了 FrameLayout
以外,ConstraintLayout
通常比其他版面配置更有效率。這麼做較不易發生多項版面配置傳遞作業,而且在許多情況下,也無需建立巢狀版面配置。
RelativeLayout
以外的容器也可能增加重複作業,例如:
- 如果將
LinearLayout
檢視區塊設為水平方向,就可能重複進行版面配置和測量傳遞。如果在垂直方向加入measureWithLargestChild
,架構可能需要執行第二次傳遞來解析物件的正確尺寸,因此也可能發生兩次的版面配置和測量傳遞。 GridLayout
也允許相對定位,但通常可以藉由預先處理子項檢視區塊間的位置關係,避免發生重複作業。不過,如果版面配置使用粗細或填滿Gravity
類別,的好處是 預先處理作業遺失,而如果容器處於某個狀態,則架構可能必須執行多次傳遞 是RelativeLayout
。
多項版面配置和測量傳遞作業不一定會對效能造成負擔。但如果位置不對,就可能成為負擔。容器符合以下任一種情況時,請務必謹慎處理:
- 容器是檢視區塊階層的根元素。
- 容器底下的檢視區塊階層較深。
- 畫面會填入容器的多個例項,類似
ListView
物件中的子項。
診斷檢視區塊階層的問題
版面配置效能是包含多個面向的複雜問題。以下工具可協助您找出效能瓶頸發生的位置。有些工具提供的資訊較不明確,但可提供實用的提示。
Perfetto
Perfetto 是一項可提供效能相關資料的工具。您可以在 Perfetto UI 中開啟這些 Android 追蹤記錄。
剖析 GPU 轉譯
如果您使用 Android 6.0 (API 級別 23) 以上版本的裝置,可透過裝置端剖析 GPU 轉譯工具獲得效能瓶頸的具體資訊。您可以利用這項工具,瞭解在版面配置和測量階段每個影格花費的轉譯時間。這項資料有助於診斷執行階段效能問題,以及判斷是否有哪些版面配置和測量問題需要解決。
剖析 GPU 轉譯會以圖形呈現擷取的資料,在其中使用藍色表示版面配置時間。如要進一步瞭解如何使用這項工具,請參閱「剖析 GPU 轉譯速度」。
Lint
Android Studio 的 Lint 工具可協助您瞭解檢視區塊階層中效率不佳的問題。如要使用這項工具,請依序選取「Analyze」>「Inspect Code」,如圖 1 所示。
![](https://developer.android.com/static/topic/performance/images/lint-inspect-code.png?authuser=9&hl=zh-tw)
在「Android」>「Lint」>「Performance」下方會顯示多個版面配置項目的資訊。如要瞭解細節,請點選展開各個項目,在畫面右側的窗格中查看詳細資訊。圖 2 是已展開資訊的範例。
![](https://developer.android.com/static/topic/performance/images/lint-display.png?authuser=9&hl=zh-tw)
只要按一下項目,即可在右側窗格中顯示該項目的相關問題。
如要進一步瞭解這部分的特定主題和問題,請參閱 Lint 說明文件。
版面配置檢查器
Android Studio 的版面配置檢查器工具提供應用程式的檢視區塊階層示意圖。這是瀏覽應用程式階層的好方法,您可以透過清楚的圖表查看特定檢視區塊的父項鏈結,便於檢查應用程式建構的版面配置。
您也可以透過版面配置檢查器顯示的檢視區塊,找出重複作業造成的效能問題。您也可以用這種方式識別巢狀版面配置的深層鏈結,或是具有大量巢狀子項的版面配置區域,這是另一個會耗用資源、影響效能的來源。在這種情況下,版面配置和測量階段可能會耗用大量資源,並導致效能問題。
詳情請參閱「使用版面配置檢查器和驗證功能,對版面配置進行偵錯」。
解決檢視區塊階層的問題
要實際解決檢視區塊階層引起的效能問題,背後的基本概念較難以實踐。要防止檢視區塊階層導致效能降低,需簡化檢視區塊階層和減少重複作業。本節會說明有助達成這些目標的策略。
移除多餘的巢狀版面配置
ConstraintLayout
是 Jetpack 程式庫,提供大量的不同機制,用於在版面配置中定位檢視畫面。這樣就不必建立巢狀 ConstaintLayout
,有助於簡化檢視區塊階層。與其他版面配置類型相比,使用 ConstraintLayout
簡化階層通常較為簡單。
開發人員經常會使用過多的巢狀版面配置。舉例來說,RelativeLayout
容器包含的單一子項可能也是 RelativeLayout
容器。這樣的巢狀結構是多餘的,會為檢視區塊階層增加不必要的資源浪費。Lint 可以標記這個問題,減少偵錯所需時間。
採用 merge 或 include 標記
使用多冗餘巢狀版面配置的其中一個原因是 <include>
標記。舉例來說,您可以定義可重複使用的版面配置,如下所示:
<LinearLayout> <!-- some stuff here --> </LinearLayout>
接著,您可以加入 <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>
標記有助於避免這個問題。如需此標記的詳細資訊,請參閱「使用 <merge> 標記」。
採用耗用資源較少的版面配置
您可能無法調整現有的版面配置設定,移除多餘的版面配置。在某些情況下,唯一的解決方式可能是改用完全不同的版面配置類型,從而簡化階層。
例如,您可能會發現
TableLayout
提供相同的
是較為複雜的版面配置,且包含許多位置依附元件。Jetpack 程式庫
ConstraintLayout
敬上
提供與 RelativeLayout
類似的功能,並且提供更多有助於建立
流暢的版面配置