Flutter:测试期间的计时器问题
Flutter: Timer Issue During Testing
我的 StatelessWidget
中有一个定期计时器。无需过多介绍,这里是生成计时器的代码片段:
class AnniversaryHomePage extends StatelessWidget {
. . .
void _updateDisplayTime(StoreInheritedWidget inheritedWidget) {
String anniversaryString = inheritedWidget.prefs.getString('anniversaryDate');
inheritedWidget.store.dispatch(DateTime.parse(anniversaryString));
}
/// Widget Methods
@override
Widget build(BuildContext context) {
final inheritedWidget = StoreInheritedWidget.of(context);
new Timer.periodic(this.refreshRate, (Timer timer) => _updateDisplayTime(inheritedWidget));
. . .
}
当我尝试将我的应用程序的起点放入 flutter test
时,我收到以下错误消息:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test:
A periodic Timer is still running even after the widget tree was disposed.
'package:flutter_test/src/binding.dart': Failed assertion: line 668 pos 7:
'_fakeAsync.periodicTimerCount == 0'
问题是,我的 Timer.periodic
是不是用错了?如果不是,我该如何减轻这个错误?
问题是创建一个计时器会创建一个必须释放的资源,因此您的小部件实际上是有状态的而不是无状态的。具体来说,build
方法每秒可能被调用 60 次(如果平台是 120fps,则可以调用更多次)。少一点只是一种优化。
您的代码中有一个非常严重的错误 - build
方法每次调用时都会创建一个新的 Timer。由于您的计时器永远不会被取消,因此您可能会将成百上千个事件发送到您的商店。
为避免出现这种情况,该框架具有 State
class,以及 initState
和 dispose
生命周期。该框架承诺,如果它重建您的小部件,它不会多次调用 initState
,并且它将始终调用 dispose
。这允许您创建一个 Timer 一次并在后续调用 build
.
时重用它
例如,您可以将大部分逻辑移动到如下状态。将 refreshRate 保留在小部件上,您甚至可以使用 didUpdateWidget
生命周期取消和更新计时器。
class AnniversaryHomePage extends StatefulWidget {
@override
State createState() => new AnniversaryHomePageState();
}
class AnniversaryHomePageState extends State<AnniversaryHomePage> {
Timer _timer;
@override
void initState() {
_timer = new Timer.periodic(widget.refreshRate,
(Timer timer) => _updateDisplayTime(inheritedWidget));
super.initState();
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
...
}
}
我的 StatelessWidget
中有一个定期计时器。无需过多介绍,这里是生成计时器的代码片段:
class AnniversaryHomePage extends StatelessWidget {
. . .
void _updateDisplayTime(StoreInheritedWidget inheritedWidget) {
String anniversaryString = inheritedWidget.prefs.getString('anniversaryDate');
inheritedWidget.store.dispatch(DateTime.parse(anniversaryString));
}
/// Widget Methods
@override
Widget build(BuildContext context) {
final inheritedWidget = StoreInheritedWidget.of(context);
new Timer.periodic(this.refreshRate, (Timer timer) => _updateDisplayTime(inheritedWidget));
. . .
}
当我尝试将我的应用程序的起点放入 flutter test
时,我收到以下错误消息:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test:
A periodic Timer is still running even after the widget tree was disposed.
'package:flutter_test/src/binding.dart': Failed assertion: line 668 pos 7:
'_fakeAsync.periodicTimerCount == 0'
问题是,我的 Timer.periodic
是不是用错了?如果不是,我该如何减轻这个错误?
问题是创建一个计时器会创建一个必须释放的资源,因此您的小部件实际上是有状态的而不是无状态的。具体来说,build
方法每秒可能被调用 60 次(如果平台是 120fps,则可以调用更多次)。少一点只是一种优化。
您的代码中有一个非常严重的错误 - build
方法每次调用时都会创建一个新的 Timer。由于您的计时器永远不会被取消,因此您可能会将成百上千个事件发送到您的商店。
为避免出现这种情况,该框架具有 State
class,以及 initState
和 dispose
生命周期。该框架承诺,如果它重建您的小部件,它不会多次调用 initState
,并且它将始终调用 dispose
。这允许您创建一个 Timer 一次并在后续调用 build
.
例如,您可以将大部分逻辑移动到如下状态。将 refreshRate 保留在小部件上,您甚至可以使用 didUpdateWidget
生命周期取消和更新计时器。
class AnniversaryHomePage extends StatefulWidget {
@override
State createState() => new AnniversaryHomePageState();
}
class AnniversaryHomePageState extends State<AnniversaryHomePage> {
Timer _timer;
@override
void initState() {
_timer = new Timer.periodic(widget.refreshRate,
(Timer timer) => _updateDisplayTime(inheritedWidget));
super.initState();
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
...
}
}