前言
最近Compose已經正式發布了1.0版本,這說明谷歌認為Compose已經可以用于正式生產環境了 那么相比傳統的XML,Compose的性能到底怎么樣呢?
本文主要從構建性能與運行時兩個方面來分析Compose的性能,資料主要來源于:Jetpack Compose — Before and after[1] 與 Measuring Render Performance with Jetpack Compose[2] , 想了解更多的同學可以直接點擊查看
構建性能
Compose構建性能主要以 tivi[3] 為例來進行說明 Tivi是一個開源的電影App,原本基于Fragment與XML構建,同時還使用了DataBinding等使用了注解處理器的框架 后來遷移到使用Compose構建UI,遷移程序分為兩步
- 第一步:遷移到
Navigation與Fragment,每個Fragment的UI則由Compose構建 - 第二步:移除
Fragment,完全基于Compose實作UI
下面我們就對Pre-Compose,Fragments + Compose,Entirely Compose三個階段的性能進行分析對比
APK體積
包體積是我們經常關注的性能指標之一,我們一起看下3個階段的包體積對比


可以看出,Tivi 的 APK 大小縮減了 46%,從 4.49MB 縮減到 2.39MB,同時方法數也減少了17%
值得注意的是,在剛開始在應用中采用Compose時,有時您會發現APK大小反而變大了 這是因為遷移沒有完成,老的依賴沒有完成移除,而新的依賴已經添加了,導致APK體積變大 而在專案完全遷移到Compose后,APK 大小會減少,并且優于原始指標,
代碼行數
我們知道在比較軟體專案時,代碼行數并不是一個特別有用的統計資料,但它確實提供了對事物如何變化的一個觀察指標, 我們使用cloc工具[4]來計算代碼行數
cloc . --exclude-dir=build,.idea,schemas
結果如下圖所示:

可以看出,在遷移到Compose后,毫無意外的,XML代碼行減少了76% 有趣的是kotlin代碼同樣減少了,可能是因為我們可以減少很多模板代碼,同時也可以移除之前寫的一些View Helper代碼
構建速度
隨著專案的不斷變大,構建速度是開發人員越來越關心的一個指標, 在開始重構之前,我們知道,洗掉大量的注解處理器會有助于提高構建速度,但我們不確定會有多少,
我們運行以下命令5次,然后取平均值
./gradlew --profile --offline --rerun-tasks --max-workers=4 assembleDebug
結果如下

這里考慮的是除錯構建時間,您在開發期間會更關注此時間,
在遷移到Compose前,Tivi 的平均構建時間為 108.71 秒, 在完全遷移到 Compose 后,平均構建時間縮短至 76.96 秒!構建時間縮短了 29%, 構建時間能縮短這么多,當然不僅僅是Compose的功勞,在很大程度上受兩個因素的影響:
- 一個是移除了使用注解處理器的
DataBinding和Epoxy - 另一個是
Hilt在AGP 7.0中的運行速度更快,
運行時性能
上面我們介紹了Compose在構建時的性能,下面來看下Compose在運行時渲染的性能怎么樣
分析前的準備
使用Compose時,可能有多種影響性能的指標
- 如果我們完全在
Compose中構建UI會怎樣? - 如果我們對復雜視圖使用
Compose(例如用LazyColumn替換RecyclerViews),但根布局仍然添加在XML中 - 如果我們使用
Compose替換頁面中一個個元素,而不是整個頁面,會怎么樣? - 是否可除錯和
R8編譯器對性能的影響有多大?
為了開始回答這些問題,我們構建了一個簡單的測驗程式, 在第一個版本中,我們添加了一個包含50個元素的串列(其中實際繪制了大約 12 個),該串列包括一個單選按鈕和一些隨機文本,

為了測驗各種選項的影響,我們添加以下4種配置,以下4種都是開啟了R8同時關閉了debug
純Compose- 一個
XML中,只帶有一個ComposeView,具體布局寫在Compose中 XML中只包含一個RecyclerView,但是RecyclerView的每一項是一個ComposeView- 純
XML
同時為了測驗build type對性能的影響,也添加了以下3種配置
- 純
Compose,關閉R8并打開debug - 純
Compose,關閉R8并關閉debug - 純
XML,關閉R8并打開debug
如何定義性能?
Compose運行時性能,我們一般理解的就是頁面啟動到用戶看到內容的時間 因此下面幾個時機對我們比較重要
Activity啟動時間,即onCreateActivity啟動完成時間,即onResumeActivity渲染繪制完成時間,即用戶看到內容的時間
onCreate與onResume的時機很容易掌握,重寫系統方法即可,但如何獲得Activity完全繪制的時間呢? 我們可以給頁面根View添加一個ViewTreeObserver,然后記錄最后一次onDraw呼叫的時間
使用Profile查看上面說的程序,如下分別為使用XML渲染與使用Compose渲染的具體程序,即從OnCreate到呼叫最后一次onDraw的程序


渲染性能分析
知道了如何定義性能,我們就可以開始測驗了
- 每次測驗都在幾臺設備上運行,包括最近的旗艦、沒有
Google Play服務的設備和一些廉價手機, - 每次測驗在同一臺手機上都會運行10次,因此我們不僅可以獲取首次渲染時間,也可以獲取二次渲染時間
- 測驗
Compose版本為1.0.0
我們根據上面定義的配置,重復跑了多次,得到了一些資料,感興趣的同學可以直接查看所有資料[5]

分析結果如上圖所示,我們可以得出一些結論
R8和是否可除錯對Jetpack Compose渲染時間產生了顯著影響,在每次實驗中,禁用R8和啟用可除錯性的構建所花費的時間是沒有它們的構建的兩倍多,在我們最慢的設備上,R8將渲染速度加快了半秒以上,而禁用debug又使渲染速度加快了半秒,XML中只包含一個ComposeView的渲染時間,跟純Compose的耗時差不多RecyclerView中包含多個ComposeView是最慢的,這并不奇怪,在XML中使用ComposeView是有成本的,所以頁面中使用的ComposeView越少越好,XML在呈現方面比Compose更快,沒有辦法解決這個問題,在每種情況下,Compose的渲染時間比XML長約 33%,- 第一次啟動總是比后續啟動花費更長的時間來渲染,如果您查看完整的資料,第一個頁面的渲染時間幾乎是后續的兩倍,
比較讓我驚訝的是,盡管Compose沒有了將XML轉化成View的IO操作,測量程序也因為固有特性測量提高了測量效率,但性能仍然比XML要差 不過,根據`Leland Richardson`[6]的說法[7],當從Google Play安裝應用程式時,由于捆綁的AOT編譯,Compose 在啟動時渲染會更快,從而進一步縮小了與XML的差距
總結
經過上面對Compose性能方面的分析,總結如下
- 如果完全遷移到
Compose,在包體積,代碼行數,編譯速度等方面應該會有比較大的改善 - 如果處于遷移中的階段,可能因為舊的依賴沒有去除,而已經引入了新的依賴,反而導致包體積等變大
- 盡管沒有了
XML轉換的IO操作,測量程序也通過固有特性測量進行了優化,Compose的渲染性能比起XML仍然有一定差距 - 盡管目前
Compose在性能方面略有欠缺(在大多數設備上僅超過一兩幀),但由于其在開發人員生產力、代碼重用和宣告式UI的強大特性等方面的優勢,Compose仍被推薦使用
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/301074.html
標籤:其他
