flutter 通過 Hot Reload 可以實現(xiàn)代碼的動態(tài)刷新,可以幫助開發(fā)者方便快速的調試代碼,構建 UI,主要是調試UI。
使用
Android Studio上直接cmd+s保存代碼就會啟動Hot Reload,需要注意的是只有 Debug 模式才能使用 Hot Reload。
工作原理
Hot Reload 只能在 Debug 模式下使用,是因為 Debug 模式下,F(xiàn)lutter 采用的是 JIT 動態(tài)編譯,代碼是運行在 Dart VM 上,JIT 將 Dart 編譯成可以運行在 Dart VM 上的 Dart Kernel,Dart Kernel 可以動態(tài)更新,所以就實現(xiàn)了代碼的實時更新功能。
當調用 Hot Reload 時:
- 首先會掃描代碼,找到上次編譯之后有變化的 Dart 代碼。
- 在將這些變化的 Dart 代碼轉化為增量的 Dart Kernel 文件。
- 將增量的 Dart Kernel 文件發(fā)送到正在移動設備上運行的 Dart VM。
- Dart VM 會將發(fā)來的增量 Dart Kernel 文件和原有的 Dart Kernel 文件合并,然后重新加載全新的 Dart Kernel。
- 重新加載了 Dart Kernel,卻不會重新執(zhí)行代碼,而是通知 Flutter Framework 重建 Widget。
所以 Flutter 的 Hot Reload 并不會重新執(zhí)行一遍代碼,而是觸發(fā) Flutter 重新繪制,并且會保留 Flutter 之前的狀態(tài),所以 Hot Reload 也被稱為有狀態(tài)的熱重載。
不能使用 Hot Reload 的場景
在理解了 Hot Reload 的原理之后,可以看到 Hot Reload 的使用場景是有一些限制的,接下來我們在看一下不能使用 Hot Reload的 場景:
1. 代碼更改會影響 APP 狀態(tài)的不能使用 Hot Reload
如果你的代碼更改會影響 APP 的狀態(tài),使得代碼更改之后的狀態(tài)和代碼更改之前的狀態(tài)不一樣,那么 Hot Reload 就不會生效,例如:
class MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
return GestureDetector(onTap: () => print('T'));
}
}
這段代碼,運行 App 之后,將 StatelessWidget 改為 StatefulWidget:
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> { /*...*/ }
因為 Hot Reload 會保留狀態(tài),在代碼更改之前,MyWidget 是 StatelessWidget ,將 StatelessWidget 改為 StatefulWidget ,如果 Hot Reload 成功,那么 MyWidget 會變成 StatefulWidget ,與它之前的狀態(tài)就會不兼容的,所以 Hot Reload 是不會成功的。
2. 全局變量( global variables)和靜態(tài)字段(static fields)的更改不能使用 Hot Reload
在 Flutter 中,全局變量和靜態(tài)字段被視為狀態(tài),因此在 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,是不會成功的,所以全局變量和靜態(tài)字段不能使用 Hot Reload。
3. main() 方法里的更改不能使用 Hot Reload
因為 main() 方法不會因重建窗口小部件樹而重新執(zhí)行,所以更改 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 之后,不會看到任何變化。
- 枚舉類型更改為常規(guī)的類或者常規(guī)的類變?yōu)槊杜e類型也不能使用 HotReload
例如,如下的例子:
enum Color {
red,
green,
blue
}
改為:
class Color {
Color(this.i, this.j);
final int i;
final int j;
}
- 修改通用類型聲明也不能使用 HotReload
例如,如下的例子:
class A<T> {
T i;
}
改為:
class A<T, V> {
T i;
V v;
}
Hot Reload VS Hot Restart
針對上面不能使用 Hot Reload 的情況,就需要使用 Hot Restart。Hot Restart 可以完全重啟您的應用程序,但卻不用結束調試會話。這個特性可以用來快速調試代碼,對于業(yè)務代碼可能會出現(xiàn) Hot Restart 失敗的情況,這時直接 Hot Reload 也就是直接run就行了,Hot Restart 大部分使用場景還是用來調整UI或者業(yè)務代碼上小而不改變狀態(tài)的改動。