使用JetPack Compose开发的App超出预期卡顿问题求助
1. 优先排查编译模式问题
这是初学者最容易踩的高频坑:Compose 的 Debug 编译模式默认开启了大量调试校验逻辑,包括重组追踪、协程调试、边界检查等,运行性能仅为 Release 模式的 10%~30%,完全不能作为性能参考。
请切换到 Release 模式编译打包,同时开启 R8 混淆和资源压缩,配置方式如下(Module 级 build.gradle 文件):
android { // 省略其他配置 buildTypes { release { // 开启R8混淆 minifyEnabled true // 开启无用资源压缩 shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } }
2. 排查可组合函数的稳定性问题
Compose 的重组优化完全依赖参数的稳定性,如果传入的参数是不稳定类型,会导致可组合函数无法跳过不必要的重组,大幅消耗性能。
常见的不稳定类型包括:没有加@Immutable注解的非数据类、普通接口、可变集合类。
可以通过开启 Compose 编译器稳定性报告排查不稳定的类,在 gradle.properties 文件中添加如下配置:
kotlin.experimental.strongly.typedCompose.enabled=true composeCompilerReports=true composeCompilerMetrics=true
重新编译项目后会在build/compose_compiler目录下生成稳定性报告,将标记为UNSTABLE的类做优化:数据类添加@Immutable注解,集合类替换为 kotlinx.collections.immutable 下的不可变集合。
3. 长列表场景专项优化
如果是长消息列表场景卡顿,优先检查 LazyColumn/LazyRow 的配置:
- 必须给 item 绑定唯一 key,不要使用下标作为 key,避免列表数据变化时全量重组:
LazyColumn { items( count = messageList.size, // 用消息本身的唯一ID作为key key = { index -> messageList[index].messageId } ) { index -> MessageItem(message = messageList[index]) } }
- 不要在 LazyColumn 的 item 作用域内执行任何计算逻辑,所有需要计算的内容都放在
remember中缓存。
4. 频繁更新的状态优化
展开消息、切换颜色这类需要频繁触发状态变化的场景,注意优化状态监听逻辑:
- 关联滚动位置、列表状态等高频变化的状态时,使用
derivedStateOf包裹,仅当判断结果变化时才触发重组,示例:
// 优化前:滚动状态每次变化都会触发重组 val showExpandBtn = scrollState.firstVisibleItemIndex > 3 // 优化后:仅当判断结果从false变true/true变false时才触发重组 val showExpandBtn by remember { derivedStateOf { scrollState.firstVisibleItemIndex > 3 } }
- 不要在可组合函数中直接执行复杂计算、IO 操作,所有耗时逻辑都必须放在 ViewModel 协程、
LaunchedEffect或者remember中缓存,避免每次重组都重复执行。
5. 针对性定位异常重组点
如果以上优化都做了还是卡顿,可以打开 Android Studio 的 Layout Inspector,开启「重组计数」功能,复现卡顿操作的同时观察各个可组合函数的重组次数,重组次数明显超出预期的函数就是卡顿根源,针对性优化即可。
内容的提问来源于stack exchange,提问作者Curious Learner

