关于 understanding-hot-reload-limits
这项技能说明 HotSwan 可热重载哪些 Kotlin 和资源修改,哪些会触发增量构建。它帮助 Android 开发者依据 ART 的类结构限制安排修改,让快速迭代集中在受支持的变更上。
主要功能
- 01区分函数体修改与结构变更
- 02识别会触发重新构建的修改
- 03说明 data class 的 API 版本差异
- 04规划修改以合并构建步骤
- 05诊断 HotSwan 回退状态
适用场景
- 01了解某项修改为何触发重新构建
- 02安排重构以保持快速迭代
- 03判断迭代期间是否暂时移除 inline
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ skydoves/ compose-performance-skills --skill understanding-hot-reload-limits安装后在 Claude Code 中输入/understanding-hot-reload-limits确认已加载。
详细介绍
了解热重载的限制:保持在快速路径内
Android Runtime(ART)对类重新定义施加了严格限制:重新定义的类必须与之前版本具有相同的结构(字段、方法签名、接口)。运行时只能修改方法体。HotSwan 会自动检测结构变化,并回退到完整增量构建,因此不会静默失败。提前了解这一界限,才能让迭代循环保持在亚秒级,而不是数秒。
何时使用此技能
- 开发者询问“为什么触发了完整重建?”“为什么没有热重载?”或“HotSwan 支持什么?”。
- PR 审查发现某项重构(参数变更、构造函数变更、提取接口、新资源 ID)会使热重载会话转为重建。
- 开发者正在规划会话,希望安排编辑顺序,让耗时较长的操作在最后进行。
- 开发者报告某项
inline fun变更“没有重新加载”,并正在排查问题。
何时不要使用此技能
- 安装或首次运行故障排查。请参阅
../setting-up-compose-hotswan/SKILL.md。 - 纯粹关于状态保留的问题(已重新加载,但状态丢失)。请参阅负责状态保留的同级技能。
- AI 迭代循环或 MCP 服务的配置。请参阅负责 AI 循环的同级技能。
先决条件
- 已按照
../setting-up-compose-hotswan/SKILL.md安装并验证 HotSwan(工具窗口状态为WATCHING,仅修改方法体即可在一秒内重新加载)。 - 熟悉 Kotlin 编译器对方法体变更和类结构变更的区分。
边界表
支持热重载(无需重建)
| 变更 | 说明 |
|---|---|
| Composable 函数体 | 函数内部的文本、颜色、修饰符、布局、控制流 |
| 非 Composable 函数体 | ViewModel 方法、映射器、实用工具、存储库 |
| 添加新的 Composable | 同一文件或新文件 |
| 重新排列 Composable | HotSwan 1.2.0+ |
| 资源值变更 | strings.xml 值、colors.xml 值、dimens.xml 值 |
| 扩展函数 | 包括 suspend 和 vararg |
添加 data class 属性 | 仅适用于 API 30+ |
| 数字、字符串、浮点数字面量补丁 | 单独编译,以实现最快重新加载 |
会触发完整重建(回退)
| 变更 | 原因 |
|---|---|
| 添加或移除函数参数 | 方法签名变更 |
| 构造函数变更 | 参数列表、默认值、影响字段的 init 块 |
| 接口或超类变更 | 类层次结构属于类结构的一部分 |
| 添加新的资源 ID | 新的 R.string、R.drawable、R.id 条目需要重新生成 R 类 |
| 内联函数 | 会在每个调用点展开;没有可单独替换的单元 |
| 函数内部的 Lambda 数量变更 | 内部 Lambda 类重新编号 |
| 移除先前定义的函数 | 方法表缩小;类结构发生变化 |
在 API 30 以下添加 data class 属性 | 构造函数结构变更,且 ART 不支持 |
更改无法热重载时的工作流
- 保存时观察 HotSwan 工具窗口的状态。 架构变更会显示“回退到增量构建”状态。这是明确的信号,并非静默失败。
- 查看更改差异。 判断这是仅修改函数体,还是架构变更(签名、构造函数、接口、字段、新资源 ID、内联函数体、lambda 数量)。
- 如果重构必须更改签名,请分批进行。 先进行仅修改函数体的更改,让屏幕显示正常,然后热重载。接着在单独一次保存中更改签名,并将重建作为本次会话的最后一步。
- 对于内联函数,迭代期间移除
inline。 迭代时将函数重构为非inline,按需热重载,并在提交前恢复inline。如果很少迭代,或必须保留性能特征,则接受重建。 - 对于新的资源 ID,先批量添加所有 ID,接受一次重建,然后迭代修改值。编辑
<string name="title">Updated Title</string>(已有 ID)会热重载;添加<string name="brand_new_id">...</string>则不会。
模式
模式:函数体变更(快速路径)与签名变更(重建)
// RIGHT (body-only change, hot-reloads)
@Composable
fun Greeting() {
Text("Hello, World", color = Color.Blue)
}
// WRONG for the fast path (signature change, forces rebuild)
@Composable
fun Greeting(name: String) {
Text("Hello, $name")
}
// WRONG because: adding a parameter changes the method signature, which violates ART's class
// schema constraint. To stay inside the fast path, introduce the parameter with a default value
// in one save (still a rebuild; accept it once), then iterate on the body across subsequent saves.
模式:添加新的可组合函数(快速路径)与更改构造函数(重建)
// RIGHT (new composable in the same file, hot-reloads on HotSwan 1.2.0+)
@Composable
fun SecondaryAction() {
Text("New")
}
// WRONG for the fast path (constructor change)
data class User(val id: Long, val name: String, val age: Int)
// WRONG because: adding `age` extends the constructor. On API 30+ HotSwan supports adding
// data class properties; below API 30 the schema change forces a rebuild. When targeting older
// minSdk, batch property additions and accept a rebuild for each.
模式:内联函数体变更(重建)
// WRONG for the fast path
inline fun <T> Modifier.observer(value: T, body: (T) -> Modifier): Modifier = body(value)
// WRONG because: inline functions are expanded at every call site at compile time. There is no
// discrete unit to swap, so editing the inline body forces a full rebuild of every call site.
// Drop `inline` for the duration of iteration, or accept the rebuild.
// RIGHT for the fast path (non-inline variant during iteration)
fun <T> Modifier.observer(value: T, body: (T) -> Modifier): Modifier = body(value)
模式:资源值变更(快速路径)与新增资源 ID(重建)
<string name="title">Updated Title</string>
<string name="brand_new_id">Hello</string>
模式:lambda 数量变更(重建)
// RIGHT (body change inside the existing lambda, hot-reloads)
@Composable
fun Row() {
Button(onClick = { log("tapped") }) { Text("Tap") }
}
// WRONG for the fast path (adds a second lambda, renumbers inner lambda classes)
@Composable
fun Row() {
Button(onClick = { log("tapped") }) { Text("Tap") }
Button(onClick = { log("second") }) { Text("Second") }
}
// WRONG because: a Kotlin function's compiled lambdas are anonymous classes named like
// Row$lambda$1, Row$lambda$2. Adding a lambda renumbers them and changes the class table,
// which is a schema change. The fix is to add the second composable as a separate top-level
// function (which is supported), then call it from Row once the structural change has rebuilt.
强制规则
- 必须在建议更改开发者正在迭代的可组合函数之前查阅边界表。如果开发者想要的是速度,建议新增参数会浪费迭代周期。
- 不得在未根据上文支持的变更表进行核对的情况下,声称某项更改会热重载。
- 不得为了“强制干净重建”而禁用 HotSwan。干净构建请使用 Gradle 的
:app:clean;HotSwan 会在需要时自动回退,关闭它会增加下一次往返的耗时。 - 必须将新增资源 ID 和内联函数体变更视为在所有 Android API 级别都会触发重建。数据类属性例外仅适用于 API 30 及以上版本。
- **优先:**安排迭代会话时,先进行仅修改函数体的编辑,并将签名、构造函数、接口或新增资源 ID 的更改集中到最后。
- **优先:**如果重构需要新增参数,先添加一次并赋予默认值(接受重建),然后在后续保存中迭代修改函数体。
验证
- 保存时,仅修改可组合函数的函数体会热重载,且工具窗口状态保持为
WATCHING。 - 为可组合函数新增参数会触发工具窗口中的回退状态。
- 新增
<string>资源会触发回退;编辑已有<string>的值则不会。 - 编辑
inline函数的函数体会触发回退;将其转换为非inline函数并编辑函数体则会热重载。 - 在运行 API 30 及以上版本的设备上,为
data class新增属性会热重载;在 API 29 及以下版本上则会触发回退。
参考资料
- HotSwan 支持的变更文档:https://github.com/skydoves/compose-hotswan-web(位于
/docs/supported-changes)。 - HotSwan 限制文档:https://github.com/skydoves/compose-hotswan-web(位于
/docs/limitations)。 - Android Runtime 类重定义(JVMTI)参考资料:https://source.android.com/docs/core/runtime/jvmti
- 相关技能
../setting-up-compose-hotswan/SKILL.md,介绍安装和首次保存到热重载的验证。
评论
支持 Markdown