背景
在android开发中,无论是基于xml开发的view体系,还是compose,开发性能,页面的性能也深深影响着我们的app,xml开发中,我们可以用Layout Inspector
(AS tools选项)进行ui界面层级的查看,用于排查我们的ui层级是否合理,是否存在页面绘制的问题。虽然有这么一个好的工具,但是Layout Inspector
在普通机型上用起来却不是那么方便,一直存在着性能问题(虽然我的开发是m1芯片的mac,但是跑起来也是非常艰难,哭😭!)
虽然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性能报告如下
生成文件解析
-
app_release-classes:当前扫描到的class的情况
-
app_release-composables.csv csv格式文件,这里我们不太关注
-
app_release-composables.txt 当前所以composable函数信息
- app_release-module.json json格式输出的当前compose中的具体信息统计
相关的数据应该很容易理解,这里就不再赘述,值得注意的是,我们可以看到上面的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属性是可变的
编译后:
带有skippable修饰
相反的,我们使用不可变的data类的时候,就可以生成可skippable
的composable函数
data class TestData(val text:String)
@Composable
fun Testing(data: TestData) {
Text(text = data.text)
}
反编译后:
这里我们做一个小结论,如果想要composable函数具有跳过重组的能力时,所依赖的外部属性必须都是stable的,常见的(Boolean,Int,Long,String,Float,Double
,函数类型等等)都是稳定的,一些默认非stable的,比如interface等类,如果我们想要提高重组的性能,就可以用@Stable修饰,告诉compose编译器生成跳过重组的逻辑!
一个小坑
不过值得注意的是,list类型,即使我们用@Stable修饰,还是被认为是unstable 所依赖的composable函数依旧是不可skip(不带有skippable)
@Composable
@Stable
fun Testing3(data: List<TestData>) {
data.forEach {
Text(text = it.text)
}
}
总结
到这里,我们应该能够使用compiler-metrics去检测我们的compose项目啦!发现我们需要优化的点,这个非常重要!
最后笔者有话说:神奇的compose系列文章其实更加偏重实战技巧与底层解析,可能对不太了解compose的朋友阅读上会产生相应的干扰,但是不要紧!继续冲就对啦!
作者:Pika
链接:https://juejin.cn/post/7139328582836289566
最后
为方便大家系统的学习 Android Compose ,这里特意联合了阿里P7架构师和谷歌技术团队共同整理了一份 《Android Compose 强化实战》全文条理清晰,含图像化表示更加易懂,非常适合想要进阶提升的伙伴,有需要者可扫描文末卡片查看获取方式!
《Android Compose 强化实战》
目录
第一章 使用Compose实现底部按钮和首页banner以及数据列表
- 第一节 Column、Row、ConstraintLayout布局先知
- 第二节 首页内容的实现
第二章 导航规整并实现登录页个人中心页
- 第一节 导航规整
- 第二节 个人中心的实现
- 第三节 登录页面的实现
第三章 实现分类页面
- 第一节 Scaffold简单使用
- 第二节 BottomNavigation和NavHost实现底部导航
- 第三节 分类页面的实现
- 第四节 Compose自定义布局实现流式布局
第四章 实现搜索页面
- 第一节 ROOM数据库
- 第二节 官方Flow Layout
- 第三节 状态布局
第五章 项目页面的实现
- 第一节 获取数据
- 第二节 Controllable实现顶部滑动菜单
- 第三节 HorizontalPager 实现页面数据列表
- 第四节 Compose中Webview的使用
由于文章内容比较多,篇幅有限,资料已经被整理成了PDF文档,有需要《Android Compose 强化实战》完整文档的可扫描下方卡片免费领取👇!!