神奇的Compose - 效能監控篇

語言: CN / TW / HK

我報名參加金石計劃1期挑戰——瓜分10萬獎池,這是我的第1篇文章,點選檢視活動詳情

背景

在android開發中,無論是基於xml開發的view體系,還是compose,開發效能,頁面的效能也深深影響著我們的app,xml開發中,我們可以用Layout Inspector(AS tools選項)進行ui介面層級的檢視,用於排查我們的ui層級是否合理,是否存在頁面繪製的問題。雖然有這麼一個好的工具,但是Layout Inspector在普通機型上用起來卻不是那麼方便,一直存在著效能問題(雖然我的開發是m1晶片的mac,但是跑起來也是非常艱難,哭😭!)雖然compose以高效能高效率被開發者接收,但是這個高效率也並不是永遠那麼“值得信賴”,因為智慧重組也是有侷限的,這部分內容可以在神奇的Compose- 開篇有介紹!所以,判斷我們compose效能是否好壞,沒錯,官方也提供了相關的檢測手段!Interpreting Compose Compiler Metrics

使用compiler-metrics

為了檢測compose-compiler的效能,compose官方也釋出了檢測外掛,可以直接在我們的build.gradle新增以下程式碼

啟動

可直接在terminal執行以下命令,用於啟用compose編譯檢測 .gradlew -Pandroidx.enableComposeCompilerMetrics=true :compose:runtime:runtime:compileKotlin 生成報告 .gradlew -Pandroidx.enableComposeCompilerReports=true :compose:runtime:runtime:compileKotlin 其他的gradle module可以配置 compileKotlin { freeCompilerArgs += listOf( "-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=<directory>" ) } compileKotlin { freeCompilerArgs += listOf( "-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=<directory>" ) } 其中directory表示我們想要生成的報告路徑,當然,以上幾種方式都不太方便我們啟用檢測,比如我們想要在release包配置不檢測,debug檢測或者其他包檢測的時候,就不是那麼方便了,所以這裡給出一個非常好的寫法,直接在根目錄build.gradle配置即可

subprojects { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions { if (project.findProperty("myapp.enableComposeCompilerReports") == "true") { freeCompilerArgs += [ "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + project.buildDir.absolutePath + "/compose_metrics" ] freeCompilerArgs += [ "-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + project.buildDir.absolutePath + "/compose_metrics" ] } } } } 上述配置了一個開關,如果想要啟用檢測,只需要把myapp.enableComposeCompilerReports這個屬性設定為true即可,比如以下 ./gradlew assembleRelease -Pmyapp.enableComposeCompilerReports=true 生成的compose效能報告如下

image.png

生成檔案解析

  • app_release-classes:當前掃描到的class的情況

image.png

  • app_release-composables.csv csv格式檔案,這裡我們不太關注
  • app_release-composables.txt 當前所以composable函式資訊

image.png

  • app_release-module.json json格式輸出的當前compose中的具體資訊統計

image.png

相關的資料應該很容易理解,這裡就不再贅述,值得注意的是,我們可以看到上面的compose函式中,有的函式被標記為 restartable,有的是skippable,有的是兩者都有,有的是隻有restartable,這個是什麼含義呢?

restartable:可組合,代表著compose中的函式是否可以參加重組,目前應該所有的幾乎所有@Composable修飾函式都可以參加重組。

skippable:是否可以跳過重組,這是什麼意思呢?在我們compose函式中,重組不是每一次都需要發生的,當此次的compose函式沒有發生變化,就會跳過重組(key相同且內容沒有變化時)我們在可以反編譯compose函式可以看到相關的函式(編譯時生成)

$composer2.skipToGroupEnd(); 這個就屬於跳過重組!這裡跟 restartable形成區別,並不是所有的composable函式都可以在編譯時生成跳過重組的方法,我們用以下對比例子來說明:

不帶有skippable修飾

class TestData2 { var text = "" } ``` @Composable fun Testing1(data: TestData2) { Text(text = data.text)

} ``` 此時該函式就不帶有skippable修飾,因為其中依賴的TestData2中的text屬性是可變的

image.png 反編譯後:

image.png

帶有skippable修飾

相反的,我們使用不可變的data類的時候,就可以生成可skippable的composable函式 data class TestData(val text:String) ``` @Composable fun Testing(data: TestData) { Text(text = data.text)

} ```

image.png 反編譯後:

image.png

這裡我們做一個小結論,如果想要composable函式具有跳過重組的能力時,所依賴的外部屬性必須都是stable的,常見的(Boolean,Int,Long,String,Float,Double,函式型別等等)都是穩定的,一些預設非stable的,比如interface等類,如果我們想要提高重組的效能,就可以用@Stable修飾,告訴compose編譯器生成跳過重組的邏輯!

一個小坑

不過值得注意的是,list型別,即使我們用@Stable修飾,還是被認為是unstable 所依賴的composable函式依舊是不可skip(不帶有skippable) ``` @Composable @Stable fun Testing3(data: List) { data.forEach { Text(text = it.text) }

} ``` image.png

總結

到這裡,我們應該能夠使用compiler-metrics去檢測我們的compose專案啦!發現我們需要優化的點,這個非常重要!

最後筆者有話說:神奇的compose系列文章其實更加偏重實戰技巧與底層解析,可能對不太瞭解compose的朋友閱讀上會產生相應的干擾,但是不要緊!繼續衝就對啦!附上compose官方連結 https://developer.android.google.cn/jetpack/androidx/releases/compose?hl=zh_cn