mcpField
U
understanding-hot-reload-limits

understanding-hot-reload-limits

github.com/skydoves开发520121 次安装19 周前更新
代码仓库

关于 understanding-hot-reload-limits

这项技能说明 HotSwan 可热重载哪些 Kotlin 和资源修改,哪些会触发增量构建。它帮助 Android 开发者依据 ART 的类结构限制安排修改,让快速迭代集中在受支持的变更上。

主要功能

  1. 01区分函数体修改与结构变更
  2. 02识别会触发重新构建的修改
  3. 03说明 data class 的 API 版本差异
  4. 04规划修改以合并构建步骤
  5. 05诊断 HotSwan 回退状态

适用场景

  1. 01了解某项修改为何触发重新构建
  2. 02安排重构以保持快速迭代
  3. 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同一文件或新文件
重新排列 ComposableHotSwan 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 不支持

更改无法热重载时的工作流

  1. 保存时观察 HotSwan 工具窗口的状态。 架构变更会显示“回退到增量构建”状态。这是明确的信号,并非静默失败。
  2. 查看更改差异。 判断这是仅修改函数体,还是架构变更(签名、构造函数、接口、字段、新资源 ID、内联函数体、lambda 数量)。
  3. 如果重构必须更改签名,请分批进行。 先进行仅修改函数体的更改,让屏幕显示正常,然后热重载。接着在单独一次保存中更改签名,并将重建作为本次会话的最后一步。
  4. 对于内联函数,迭代期间移除 inline。 迭代时将函数重构为非 inline,按需热重载,并在提交前恢复 inline。如果很少迭代,或必须保留性能特征,则接受重建。
  5. 对于新的资源 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 及以下版本上则会触发回退。

参考资料

评论

支持 Markdown