热重载(Hot Reload)
1. 什么是热重载
热重载是 Flutter 的一个重要特性,它允许在应用运行时动态更新代码,而无需重启应用。这大大提高了开发效率。
2. 工作原理
热重载的实际流程是:
- flutter tool 扫描变更的 Dart 文件,增量编译成 kernel
- 通过 Dart VM Service 把新的 kernel 注入正在运行的 Dart VM
- VM 对所有
State调用reassemble(),触发整树 rebuild main()和initState()不会重新执行,已有状态原样保留
3. 使用场景
热重载适用于:
- UI 布局调整
- 添加新的 Widget
- 修改 Widget 属性
- 更新业务逻辑
热重启(Hot Restart)
1. 什么是热重启
热重启会重新运行应用程序,但比完全重启要快,因为它复用了已编译的代码。
2. 工作原理
热重启的实际流程是:
- Dart VM 销毁当前 isolate,状态全部清空
- 已编译好的 kernel 被复用(这就是它比冷启动快的原因),重新执行
main() - 整个应用从头初始化,所有状态重新生成
注意:热重启只重载 Dart 层。改了原生代码(Kotlin/Swift/C++)、pubspec 依赖或
main()函数本身,必须完全重新运行,热重启也救不了。
3. 使用场景
热重启适用于:
- 修改应用全局状态
- 更改静态变量
- 需要完全重置应用状态
- 修改了
main()函数或initState里的初始化逻辑(热重载不会重新执行它们)
改原生代码、加依赖、改资源文件,走热重启也没用,必须 stop 后重新
flutter run。
实现机制
热重载不是”重新跑一遍应用”,它的核心是 Dart VM 的代码热替换能力:
- 开发期通道:
flutter run启动后,flutter tool 与设备上的 Dart VM 之间通过 VM Service(DevTools 也用同一协议)保持连接,终端里按r或 IDE 点闪电按钮,就是往这个通道发reloadSources请求。 - 增量编译:只有变更的 Dart 库被重新编译成 kernel,注入正在运行的 isolate,未变更的代码原样保留——所以热重载通常 1 秒内完成。
- 状态保留:VM 注入新代码后,对所有
State调用reassemble()触发 rebuild,但State对象本身不重建,initState()不再执行,成员变量的值原样保留。 - 热重启的区别:热重启走的是”重置 isolate + 复用 kernel 重跑
main()”,状态清零但省掉了重新编译的时间,所以比冷启动快、比热重载慢。
热重载不生效的典型情况
- 改了
main()函数或全局变量初始化 - 改了
initState()里的逻辑(不会重新执行) - 把
enum改成class这类改变类型层级结构的修改 - 改了原生代码、pubspec.yaml 或资源文件
这些情况请直接用热重启(R)或完全重新运行。
最佳实践
- 调 UI 用热重载(
r),调状态用热重启(R):这是最基本的一分二。凡是涉及初始化逻辑、单例、全局状态的修改,别跟热重载较劲,直接R。 - 把”可热重载”写进代码结构:初始化逻辑尽量收敛到
initState或单例的init()里,而不是散落在 build 中——这样热重载后的 rebuild 行为是可预测的。 - 用
const构造函数:热重载触发整树 rebuild,constwidget 会被复用,能明显减少重载后的卡顿。 - 记录重载耗时:热重载如果经常超过 3 秒,说明变更的代码被太多文件依赖,考虑拆分大文件——这是代码异味的信号,不是工具的问题。
- 状态丢失先查
reassemble:自定义的State如果重写了reassemble()做了特殊处理,热重载行为可能跟预期不一致,先看这里。
常见问题和解决方案
1. 热重载失败
常见原因:
- 语法错误
- 状态初始化问题
- 资源文件更改
解决方案:
- 检查代码语法
- 使用热重启
- 重新运行应用
2. 状态丢失
预防措施:
- 使用状态管理框架
- 实现状态持久化
- 合理使用 const 构造函数
3. 性能问题
优化建议:
- 减少不必要的重建
- 使用 const Widget
- 优化构建方法
实战案例
做 flutter_brightness(Windows 桌面应用:串口读光照传感器、用 win32 调系统亮度)时,我对热重载的边界体会特别深。改 UI 布局、调 lib/utils/brightness_mapping.dart 里映射算法的参数,按 r 秒级生效;但只要动了 serial_service.dart 里跟 flutter_libserialport、win32、ffi 相关的初始化逻辑——比如改波特率 9600 的配置——热重载经常不生效,必须热重启:原生层的串口句柄和 FFI 绑定已经建好,Dart 侧重载换不掉。教训:调硬件相关代码别跟热重载较劲,直接按 R 热重启,反而更快。
总结
Flutter 的热重载和热重启机制极大地提升了开发效率:
- 热重载提供快速的开发反馈
- 热重启解决复杂的状态问题
- 合理使用可提高开发效率
- 了解原理有助于解决问题
掌握这些机制的工作原理和最佳实践,可以帮助我们更高效地进行 Flutter 开发。
