🧩 Compose 编译器与稳定性推断 卡顿排查实战

📌 编译器如何推断稳定性 · 常见不稳定模式 · 编译器报告 ⚡ 性能优化必修课

🔍 1. 编译器如何推断稳定性

Compose 编译器在编译期对函数参数进行 稳定性分析,决定是否需要跳过重组。核心规则:
✅ 稳定类型(Stable):基本类型、String、函数类型、以及所有属性都是稳定的 data class。
❌ 不稳定类型(Unstable):接口、可变集合、var 属性、MutableState 包装不当等。

稳定性类别编译器行为示例
稳定 (Stable)允许智能重组,参数不变时跳过Int, String, data class 全val
不稳定 (Unstable)每次重组都重新执行,可能卡顿List<String>, interface, var 字段
不可变 (Immutable)编译器信任其永不改变,最强优化String, Int, 枚举

推断流程:编译器扫描 Composable 函数参数,检查每个参数类型是否满足稳定条件。若所有参数稳定,则函数可“跳过重组”;否则每次状态变化都会强制重组。

⚠️ 2. 常见导致不稳定的模式

  • 使用接口作为参数 — 编译器无法知道接口实现类的稳定性。
    ❌ 不稳定 fun MyComposable(list: List<String>) 因为 List 是接口。
  • 可变集合 / 可变状态暴露 — MutableList、ArrayList 等。
    ❌ 不稳定 val items = mutableListOf("a")
  • data class 含有 var 属性 — 编译器认为属性可能变化,标记为不稳定。
    data class User(var name: String) → 不稳定。
  • 非基本类型包装不当 — 例如 object 但内部有可变状态。
  • lambda 捕获不稳定变量 — 导致 lambda 本身不稳定。

📊 稳定性影响对比

模式稳定性重组开销
data class User(val name: String)稳定低 (可跳过)
data class User(var name: String)不稳定高 (每次重组)
List<String> (接口)不稳定高
ImmutableList<String> (Kotlinx)稳定低

📋 3. 使用编译器报告检查稳定性

在 build.gradle.kts 中启用 Compose 编译器报告,生成 稳定性推断报告:

// 模块级 build.gradle.kts
composeCompiler {
    reportsDestination = layout.buildDirectory.dir("compose-reports")
    metricsDestination = layout.buildDirectory.dir("compose-metrics")
}

运行 Gradle 任务后,在 build/compose-reports 下生成 *-classes.txt 和 *-composables.txt。

🔎 报告解读示例

// composables.txt 片段
restartable skippable fun MyScreen(
    stable user: User
    unstable list: List<String>
    unstable onClick: Function0<Unit>
)

// classes.txt 片段
unstable class User {
    unstable var name: String
}
stable class Product {
    stable val id: Int
    stable val title: String
}

关键字段:restartable(可重启)、skippable(可跳过)、stable / unstable。若函数标记为 skippable 且参数稳定,则重组时可跳过。

Composable 函数 参数稳定性检查 生成报告 ✅ 所有参数稳定 → skippable + restartable ⚠️ 存在不稳定参数 → 仅 restartable,不可跳过 📄 编译器报告 classes + composables

🧰 实战建议 · 稳定化技巧

  • 使用 @Stable 或 @Immutable 注解标记自定义类(仅当真的不可变时)。
  • 用 kotlinx.collections.immutable 替代可变集合。
  • data class 全部使用 val,避免 var。
  • 接口参数尽量改为具体稳定类型,或使用 @Stable 标记接口。
  • 定期检查编译器报告,定位不稳定热点。
// 稳定化示例
@Immutable
data class Product(
    val id: Int,
    val name: String,
    val tags: ImmutableList<String>   // 稳定集合
)

@Composable
fun ProductCard(product: Product) {  // 编译器推断稳定 → skippable
    // ...
}