Flutter - Hot Reload

flutter 通过 Hot Reload 可以实现代码的动态刷新,可以帮助开发者方便快速的调试代码,构建 UI,主要是调试UI。

使用

Android Studio上直接cmd+s保存代码就会启动Hot Reload,需要注意的是只有 Debug 模式才能使用 Hot Reload。

工作原理

Hot Reload 只能在 Debug 模式下使用,是因为 Debug 模式下,Flutter 采用的是 JIT 动态编译,代码是运行在 Dart VM 上,JIT 将 Dart 编译成可以运行在 Dart VM 上的 Dart Kernel,Dart Kernel 可以动态更新,所以就实现了代码的实时更新功能。

当调用 Hot Reload 时:

  • 首先会扫描代码,找到上次编译之后有变化的 Dart 代码。
  • 在将这些变化的 Dart 代码转化为增量的 Dart Kernel 文件。
  • 将增量的 Dart Kernel 文件发送到正在移动设备上运行的 Dart VM。
  • Dart VM 会将发来的增量 Dart Kernel 文件和原有的 Dart Kernel 文件合并,然后重新加载全新的 Dart Kernel。
  • 重新加载了 Dart Kernel,却不会重新执行代码,而是通知 Flutter Framework 重建 Widget。

所以 Flutter 的 Hot Reload 并不会重新执行一遍代码,而是触发 Flutter 重新绘制,并且会保留 Flutter 之前的状态,所以 Hot Reload 也被称为有状态的热重载。

不能使用 Hot Reload 的场景

在理解了 Hot Reload 的原理之后,可以看到 Hot Reload 的使用场景是有一些限制的,接下来我们在看一下不能使用 Hot Reload的 场景:

1. 代码更改会影响 APP 状态的不能使用 Hot Reload

如果你的代码更改会影响 APP 的状态,使得代码更改之后的状态和代码更改之前的状态不一样,那么 Hot Reload 就不会生效,例如:

class MyWidget extends StatelessWidget {
  Widget build(BuildContext context) {
    return GestureDetector(onTap: () => print('T'));
  }
}

这段代码,运行 App 之后,将 StatelessWidget 改为 StatefulWidget:

class MyWidget extends StatefulWidget {
  @override
  State createState() => MyWidgetState();
}

class MyWidgetState extends State { /*...*/ }

因为 Hot Reload 会保留状态,在代码更改之前,MyWidget 是 StatelessWidget ,将 StatelessWidget 改为 StatefulWidget ,如果 Hot Reload 成功,那么 MyWidget 会变成 StatefulWidget ,与它之前的状态就会不兼容的,所以 Hot Reload 是不会成功的。

2. 全局变量( global variables)和静态字段(static fields)的更改不能使用 Hot Reload

在 Flutter 中,全局变量和静态字段被视为状态,因此在 Hot Reload 期间不会重新初始化。

如下的代码:

final sampleTable = [
Table("T1"),
Table("T2"),
Table("T3"),
Table("T4"),
];
运行 App 之后,如果做了如下的更改:

final sampleTable = [
Table("T1"),
Table("T2"),
Table("T3"),
Table("T10"), // 修改这里的值
];
运行 Hot Reload,是不会成功的,所以全局变量和静态字段不能使用 Hot Reload。

3. main() 方法里的更改不能使用 Hot Reload

因为 main() 方法不会因重建窗口小部件树而重新执行,所以更改 main() 方法里的代码,不会在 Hot Reload 之后看到效果。

例如,如下的代码:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  Widget build(BuildContext context) {
    return GestureDetector(onTap: () => print('tapped'));
  }
}

在运行App后,更改如下:

import 'package:flutter/widgets.dart';

void main() {
  runApp(const Center(
      child: const Text('Hello', textDirection: TextDirection.ltr)));
}

Hot Reload 之后,不会看到任何变化。

  1. 枚举类型更改为常规的类或者常规的类变为枚举类型也不能使用 HotReload
    例如,如下的例子:
enum Color {
  red,
  green,
  blue
}

改为:

class Color {
  Color(this.i, this.j);
  final int i;
  final int j;
  }
  1. 修改通用类型声明也不能使用 HotReload
    例如,如下的例子:
class A {
  T i;
}

改为:

class A {
  T i;
  V v;
}

Hot Reload VS Hot Restart

针对上面不能使用 Hot Reload 的情况,就需要使用 Hot Restart。Hot Restart 可以完全重启您的应用程序,但却不用结束调试会话。这个特性可以用来快速调试代码,对于业务代码可能会出现 Hot Restart 失败的情况,这时直接 Hot Reload 也就是直接run就行了,Hot Restart 大部分使用场景还是用来调整UI或者业务代码上小而不改变状态的改动。

你可能感兴趣的:(Flutter - Hot Reload)