遇到导致性能下降的不稳定的类时 但您应确保其稳定本文档简要介绍了 方法。
启用强劲跳过
您应首先尝试启用强跳过模式。强跳过模式 允许跳过参数不稳定的可组合项,这是最容易的 方法,以修复由稳定性导致的性能问题。
如需了解详情,请参阅强跳过。
将类设为不可变
您还可以尝试使不稳定的类完全不可变。
- 不可变:表示任何属性的值都绝不可能的类型
在该类型的实例构造完成后进行更改,并且所有方法都是
引用透明
- 确保该类的所有属性均为
val
而不是var
, 和不可变类型的 String, Int
和Float
等基元类型始终是不可变的。- 如果无法做到这一点,您必须使用 Compose 状态, 任何可变的属性。
- 确保该类的所有属性均为
- 稳定:表示可变类型。Compose 运行时 了解该类型的任何公共属性或方法 行为会产生与上一次调用不同的结果。
不可变集合
Compose 认为类不稳定的一个常见原因是集合。如前所述
诊断稳定性问题页面中,Compose 编译器
无法完全确定List, Map
和Set
等集合
绝对不可变,因此将其标记为不稳定。
为了解决此问题,您可以使用不可变集合。Compose 编译器 支持 Kotlinx 不可变集合。这些 集合保证不可变,并且 Compose 编译器会将集合 如此一来此库仍处于 Alpha 版阶段,因此其 API 可能会有所变化。
再次考虑从诊断稳定性问题 问题指南:
unstable class Snack {
…
unstable val tags: Set<String>
…
}
您可以使用不可变集合使 tags
变得稳定。在课程中,将
从 tags
的类型更改为 ImmutableSet<String>
:
data class Snack{
…
val tags: ImmutableSet<String> = persistentSetOf()
…
}
这样做之后,该类的所有参数都不可变,并且 Compose 编译器会将类标记为稳定。
使用 Stable
或 Immutable
进行注解
解决稳定性问题的一种可行方法是为不稳定的类添加注解
并使用 @Stable
或 @Immutable
。
为类添加注解会替换编译器本该执行的操作
推断您的课程。它类似于
Kotlin 中的 !!
运算符。您应该非常
请谨慎使用这些注释替换编译器行为
可能会导致不可预知的 bug,例如在
完全符合预期
如果可以在不使用注解的情况下使类保持稳定,那么您应该 并努力实现稳定性
以下代码段提供了一个数据类的最小示例,该数据类带有 不可变:
@Immutable
data class Snack(
…
)
无论您使用 @Immutable
还是 @Stable
注解,Compose 编译器
将 Snack
类标记为稳定。
集合中带注解的类
假设有一个可组合项包含 List<Snack>
类型的形参:
restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
…
unstable snacks: List<Snack>
…
)
即使您为 Snack
添加了 @Immutable
注解,Compose 编译器仍会将
HighlightedSnacks
中的 snacks
参数不稳定。
就集合类型而言,参数与类存在同样的问题,
Compose 编译器始终将 List
类型的参数标记为不稳定,即使
。
您不能将单个参数标记为稳定,也不能为 始终可跳过有多条向前路径。
您可以通过多种方式解决集合不稳定的问题。 以下小节概述了这些不同的方法。
配置文件
如果您愿意遵守代码库中的稳定性合同,那么
您可以选择将 Kotlin 集合视为稳定,方法是添加
kotlin.collections.*
已添加到您的
稳定性配置文件。
不可变集合
为了保证不可变性的编译时安全,您可以
使用 kotlinx 不可变集合,而不是 List
。
@Composable
private fun HighlightedSnacks(
…
snacks: ImmutableList<Snack>,
…
)
Wrapper
如果您无法使用不可变集合,则可以创建自己的集合。为此,
将 List
封装在带有注解的稳定类中。通用封装容器很可能是
最佳选择,具体取决于您的要求。
@Immutable
data class SnackCollection(
val snacks: List<Snack>
)
然后,您可以将其用作可组合项中的形参类型。
@Composable
private fun HighlightedSnacks(
index: Int,
snacks: SnackCollection,
onSnackClick: (Long) -> Unit,
modifier: Modifier = Modifier
)
解决方案
采用上述任一方法后,Compose 编译器现在会将
HighlightedSnacks
可组合为 skippable
和 restartable
。
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
stable index: Int
stable snacks: ImmutableList<Snack>
stable onSnackClick: Function1<Long, Unit>
stable modifier: Modifier? = @static Companion
)
在重组期间,Compose 现在可以跳过 HighlightedSnacks
,前提是它的
输入的内容发生更改。
稳定性配置文件
从 Compose Compiler 1.5.5 开始,
可在编译时提供。这样,就可以考虑
不由您控制的类,例如标准库类
(如 LocalDateTime
)稳定。
配置文件是一个纯文本文件,每行包含一个类。评论、 支持单通配符和双通配符。 示例配置如下所示:
// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider kotlin collections stable
kotlin.collections.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>
如需启用此功能,请将配置文件的路径传递给 Compose 编译器选项
Groovy
kotlinOptions {
freeCompilerArgs += [
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
project.absolutePath + "/compose_compiler_config.conf"
]
}
Kotlin
kotlinOptions {
freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
"${project.absolutePath}/compose_compiler_config.conf"
)
}
由于 Compose 编译器会在项目中的每个模块上分别运行,因此您可以 根据需要为不同模块提供不同的配置。或者,设置一个 并在项目的根级目录下配置,并将该路径传递给 模块。
多个模块
另一个常见问题涉及多模块架构。Compose 编译器 只有指定某个类的所有非基元类型时, 它引用的模块明确标记为稳定, 使用 Compose 编译器构建而成。
如果您的数据层与界面层位于不同的模块中(也就是 这可能是您遇到的问题。
解决方案
要解决此问题,您可以采用以下方法之一:
- 将类添加到 Compiler 配置文件中。
- 在数据层模块上启用 Compose 编译器,或标记类
@Stable
或@Immutable
。- 这涉及向数据层添加 Compose 依赖项。不过,
它只是 Compose 运行时的依赖项,
Compose-UI
。
- 这涉及向数据层添加 Compose 依赖项。不过,
它只是 Compose 运行时的依赖项,
- 在界面模块中,将您的数据层类封装在界面专用的封装容器中 类。
如果外部库不使用 Compose 编译器。
并非每个可组合项都应该可跳过
努力解决稳定性问题时,不应试图每次 可跳过的广告这样做可能会导致优化过早
在很多情况下,可跳过并没有真正带来任何好处 并且可能导致代码难以维护。例如:
- 不经常重组或根本不重组的可组合项。
- 本身仅调用可跳过式可组合项的可组合项。
- 具有大量参数且具有开销大的 equals 方法的可组合项 实现。在这种情况下,检查任何参数是否具有 可能会超过低成本重组的成本。
当可组合项是可跳过的时,它会增加少量开销,这可能不值得 。您甚至可以为可组合项添加注解,使其成为不可重启的 这种情况下,您需要确定可重启的开销大于其价值。