自從在17年GoogleI/O大會宣布了Constraintlayout,我們持續提升了布局的穩定性和布局編輯的支持。我們還為ConstraintLayout添加了一些新特性支持創建不同類型的布局,添加這些新特性,可以明顯的提升性能,在這里,我門將討論ContrainLayout是如何提升性能的。
Android是怎么繪制view的?
為了更好的理解constrainLayout 的性能,我們先回顧android是怎么繪制視圖的。
當一個用戶把一個視圖呈現在眼前,android framework 直接讓視圖繪制自己,繪制的過程包括三個階段:
measure
系統完成視圖樹的自頂向下遍歷,以確定每個ViewGroup和View元素應該有多大。 當一個ViewGroup被測量時,它也測量它的子項。Layout
另一個自上而下遍歷行為,每個ViewGroup使用在測量階段確定的大小來確定其子級的位置。Draw
系統執行另一個自上而下的遍歷。 對于視圖樹中的每個對象,都會創建一個Canvas對象,以向GPU顯示繪圖命令列表。 這些命令包括ViewGroup和View對象的大小和位置,系統在前兩個階段確定的。
繪圖過程中的每個階段都需要對視圖樹進行自頂向下的遍歷。 因此,嵌入到視圖層次結構中的視圖越多,設備繪制視圖所需的時間和計算能力就越多。 通過在您的Android應用布局中保持扁平的層次結構,您可以為您的應用創建快速響應的用戶界面。
傳統布局結構的開銷
為了對著這個觀點的說明,讓我們創建一個使用LinearLayout和RelativeLayout對象的傳統布局層次結構。
當我們想實現上面的界面,如果我們使用傳統布局,XML布局文件包含下面的元素等級
<RelativeLayout><ImageView /><ImageView /><RelativeLayout><TextView /><LinearLayout><TextView /><RelativeLayout><EditText /></RelativeLayout></LinearLayout><LinearLayout><TextView /><RelativeLayout><EditText /></RelativeLayout></LinearLayout><TextView /></RelativeLayout><LinearLayout ><Button /><Button /></LinearLayout>
</RelativeLayout>
盡管在這種類型的視圖層次結構中通常有改進的空間,但您幾乎可以肯定仍然需要使用一些嵌套的視圖創建一個層次結構。
如前所述,嵌套層次結構可能會對性能產生不利影響。 讓我們使用Android Studio的Systrace工具來看看嵌套視圖如何實際影響UI性能。 我們以編程方式調用每個ViewGroup(ConstraintLayout和RelativeLayout)的度量和布局階段,并在度量和布局調用執行時觸發Systrace。 以下命令會生成一個概覽文件,其中包含20秒間隔內發生的關鍵事件(例如,昂貴的度量/布局傳遞):
python $ANDROID_HOME/platform-tools/systrace/systrace.py --time=20 -o ~/trace.html gfx view res
Systrace會自動突出顯示此布局的(許多)性能問題,以及修復這些問題的建議。 通過點擊“警報”選項卡,您會發現繪制該視圖層次結構需要80個昂貴的測量和布局階段!
觸發許多昂貴的措施和布局階段是不是很理想; 如此大量的繪圖活動可能導致用戶注意到的跳幀。 我們可以得出這樣的結論:由于嵌套層次結構會導致布局性能較差,像RelativeLayout每次會測量子節點兩次。
ConstraintLayout 對象的好處
如果你使用ConstrainLayout 去創建相同的布局,XML文件會包含下面的元素架構.
<android.support.constraint.ConstraintLayout><ImageView /><ImageView /><TextView /><EditText /><TextView /><TextView /><EditText /><Button /><Button /><TextView />
</android.support.constraint.ConstraintLayout>
如本例所示,布局現在具有完全平坦的層次結構。 這是因為ConstraintLayout允許您構建復雜的布局,而不必嵌套View和ViewGroup元素。
舉個例子:我們看一下當一個TextView和一個EditText在一個布局的中間。
當我們使用RelativeLayout, 你需要創建一個新的ViewGroup來使EditText和TextView垂直對齊:
<LinearLayout
android:id="@+id/camera_area"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:layout_below="@id/title" ><TextView
android:text="@string/camera"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:id="@+id/cameraLabel"android:labelFor="@+id/cameraType"android:layout_marginStart="16dp" /><RelativeLayout
android:layout_width="match_parent"android:layout_height="wrap_content"><EditText
android:id="@+id/cameraType"android:ems="10"android:inputType="textPersonName"android:text="@string/camera_value"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginTop="8dp"android:layout_marginStart="8dp"android:layout_marginEnd="8dp" /></RelativeLayout>
</LinearLayout>
通過使用ConstraintLayout來代替,只需從TextView的基線向EditText的基線添加一個約束而不創建另一個ViewGroup即可達到相同的效果:
<TextView
android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintLeft_creator="1"app:layout_constraintBaseline_creator="1"app:layout_constraintLeft_toLeftOf="@+id/activity_main_done"
app:layout_constraintBaseline_toBaselineOf="@+id/cameraType" />
在使用ConstraintLayout的布局版本上運行Systrace工具時,您會看到在相同的20秒間隔內,測量/布局合格率要低得多。 性能的改善是有意義的,現在我們保持視圖層次平坦!
在相關說明中,我們僅使用布局編輯器構建了布局的ConstraintLayout變體,而不是手動編輯XML。 為了使用RelativeLayout實現相同的視覺效果,我們可能需要手動編輯XML。
Measuring 的性能不同
我們通過使用Android 7.0(API級別24)中引入的OnFrameMetricsAvailableListener,分析了每種度量和布局通過兩種類型的布局ConstraintLayout和RelativeLayout所花費的時間。 此類允許您收集關于應用UI渲染的逐幀時間信息。
通過調用以下代碼,您可以開始記錄每幀UI操作:
window.addOnFrameMetricsAvailableListener(frameMetricsAvailableListener, frameMetricsHandler);
定時信息變為可用后,應用程序將觸發frameMetricsAvailableListener()回調。 我們對度量/布局性能感興趣,因此在檢索實際幀持續時間時我們調用FrameMetrics.LAYOUT_MEASURE_DURATION。
Window.OnFrameMetricsAvailableListener {_, frameMetrics, _ ->val frameMetricsCopy = FrameMetrics(frameMetrics);// Layout measure duration in nanosecondsval layoutMeasureDurationNs = frameMetricsCopy.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION);
要了解有關FrameMetrics可以接收的其他類型的持續時間信息的更多信息,請參閱FrameMetrics API參考。
測試結果:ConstraintLayout is faster
我們的性能比較顯示,ConstraintLayout在度量/布局階段比RelativeLayout好40%左右:
正如這些結果所示,ConstraintLayout可能比傳統布局更具性能。 此外,ConstraintLayout還有其他一些功能可以幫助您構建復雜和高性能的布局,正如在ConstraintLayout對象部分中所討論的。 有關詳細信息,請參閱使用ConstraintLayout指南構建響應式UI。 我們建議您在設計應用程序的布局時使用ConstraintLayout。 幾乎在所有情況下,如果以前需要深度嵌套的布局,ConstraintLayout應該是您的最佳布局,以實現最佳性能和易用性。
附錄:測量環境
以上所有測量均在以下環境中進行。
device | Nexus 5X |
---|---|
android version | 8.0 |
contraintLayout version | 1.0.2 |