我们在上一章回中介绍了"再谈showMenu的用法",本章回中将介绍如何实现每隔一段时间执行某项任务.闲话休提,让我们一起Talk Flutter吧。
1. 概念介绍
在实际项目中会有定时执行任务的需求,比如每隔1秒去发送网络心跳包,对于这样的需求,可以通过Stream.periodic(Duration,(){}).take(times)来实现,
再配合StreamProvider或者StreamBuilder就可以监听Stream中的事件。本章回中将介绍详细的使用方法。
2. 方法与原理
2.1 实现方法
介绍完概念后,我们看看如何实现这个概念,下面是具体的实现方法:
- 创建一个Stream,并且调用它的priodic方法来执行定期任务;
- 创建一个StreamBuilder,用来监听Stream中的事件;
- 通过StreamBuilder中的buidler属性获取Stream中的事件;
- 获取事件后,可以依据事件中的数据实现相关的逻辑业务处理;
2.2 实现原理
上面的实现方法中使用StreamBuilder来监听Stream中的事件,这个容易理解,我们重点看看Stream是如何实现每隔一段时间执行某项任务的,下面是它的源代码:
factory Stream.periodic(Duration period,
[T computation(int computationCount)?]) {
if (computation == null && !typeAcceptsNull<T>()) {
throw ArgumentError.value(null, "computation",
"Must not be omitted when the event type is non-nullable");
}
var controller = _SyncStreamController<T>(null, null, null, null);
// Counts the time that the Stream was running (and not paused).
Stopwatch watch = new Stopwatch();
controller.onListen = () {
int computationCount = 0;
void sendEvent(_) {
watch.reset();
if (computation != null) {
T event;
try {
event = computation(computationCount++);
} catch (e, s) {
controller.addError(e, s);
return;
}
controller.add(event);
} else {
controller.add(null as T); // We have checked that null is T.
}
}
Timer timer = Timer.periodic(period, sendEvent);
controller
..onCancel = () {
timer.cancel();
return Future._nullFuture;
}
..onPause = () {
watch.stop();
timer.cancel();
}
..onResume = () {
Duration elapsed = watch.elapsed;
watch.start();
timer = new Timer(period - elapsed, () {
timer = Timer.periodic(period, sendEvent);
sendEvent(null);
});
};
};
return controller.stream;
}
从上面的源代码中可以看到,该代码主要通过Timer来实现定期执行某项任务,不过它也有一些局限性,因为event的值递增的(computation(computationCount++))。如果使用StreamProvider指定初始值,但是它还会回到原来的默认值,比如指定泛型为int,初始值为3,它开始运行时值为3,指定时间到了后又变成了默认值0.此后
这个值进行递增,直到执行times次后停止。
///自己实现的计时器,和Stream.periodic()方法的原理相同,不同之处在于可以控制事件的逻辑,Stream中只能是做加法
TextButton(
onPressed: () {
Timer.periodic(const Duration(seconds: 1,), (timer) {
setState(() {
countdownTime++;
if(countdownTime == 5) {
timer.cancel();
}
});
});
},
child: countdownTime == 0 ? const Text("Start") : Text(countdownTime.toString()),
),
///模拟上一个内容,做成倒计时,不过需要先启动上一个按钮,加到5以后才能启动下一个按钮
TextButton(
onPressed: () {
Timer.periodic(const Duration(seconds: 1,), (timer) {
setState(() {
countdownTime--;
if(countdownTime == 0) {
timer.cancel();
}
});
});
},
child: countdownTime != 0 ? const Text("Start") : Text(countdownTime.toString()),
),