前言
之前在使用原生的 Timer 定时器实现定时任务时,发现当切换页面或应用切换至后台时,Timer 也会被冻结。如何实现可全局调用,支持后台执行的定时任务呢?我想到了使用 taskpool 任务池实现定时任务,在熟练使用 taskpool 实现定时任务后,进一步将其封装为一个自定义 Timer 工具类方便复用。
实现技术
- 基于 API 12 ,理论上 13 和 14 版本也可以正常使用;
- taskpool 多线程任务池,原生 Timer 的替代方案,该 API 下有 executeDelayed() 和 executePeriodically() 方法分别可以实现延时任务和周期任务;
- emitter 事件订阅,类似安卓开发的 eventbus。由于 taskpool 构造时传入的函数,仅允许序列化支持类型作为入参,于是借助 emitter 做一层中转,通知到自定义 Timer 类触发相应的回调。
实现代码
新建文件 FuTimer.ets,所有实现代码写在同一文件下就好。
- 设计定时器的回调接口,在接收到 onTask() 回调时,调用需要定时/周期性执行的任务方法:
typescript
export interface TimerCallback {
onStart: () => void; // 定时器开始
onCancel: () => void; // 定时器取消
onTask: () => void; // 定时任务触发时的回调
}
- 自定义工具类 Timer 实现:
kotlin
import { taskpool } from "@kit.ArkTS"
import { BusinessError, emitter, systemDateTime } from "@kit.BasicServicesKit";
export class Timer {
public constructor(callback: TimerCallback) {
this.timerName = `futimer_${systemDateTime.getTime()}`;
this.timerCallback = callback;
emitter.on(this.timerName, () => {
if (this.timerCallback) {
this.timerCallback.onTask();
}
});
}
private timerName: string; // 自定义 Timer 唯一名称,与 emitter 订阅事件绑定
private timerTask: taskpool.Task | undefined;
private timerCallback: TimerCallback | undefined;
/*
* isRepeat: 是否重复执行任务,false 只执行一次, true 会周期性地执行
* interval:任务执行间隔时间
*/
public start(isRepeat: boolean, interval: number) {
// 取消该定时器上一个任务
if (this.timerTask) {
this.cancel();
}
// 开始执行当前任务
if (this.timerCallback) {
this.timerCallback.onStart();
} else {
return;
}
// 重置定时任务
this.timerTask = new taskpool.Task(executeTask, this.timerName);
if (isRepeat) {
taskpool.executePeriodically(interval, this.timerTask);
} else {
taskpool.executeDelayed(interval, this.timerTask);
}
}
public cancel() {
try {
if (this.timerTask) {
taskpool.cancel(this.timerTask);
}
} catch (e) {
let error = e as BusinessError;
} finally {
this.timerTask = undefined;
if (this.timerCallback) {
this.timerCallback.onCancel();
}
}
}
// 销毁定时器,此时再调用 start() 方法无效
public destroy() {
this.cancel();
emitter.off(this.timerName);
this.timerTask = undefined;
this.timerCallback = undefined;
}
}
export interface TimerCallback {
onStart: () => void;
onCancel: () => void;
onTask: () => void;
}
@Concurrent
function executeTask(timerName: string) {
emitter.emit(timerName);
}
ps:工具类我写在模块下,需要在模块的 Index.ets 中将其 export 出来:
javascript
export * as futimer from './src/main/ets/utils/FuTimer'
调用示例
现在使用封装好的定时器工具,实现一个简单的计时功能:
typescript
import { futimer } from 'fusdk'; // fusdk 是我定义的本地模块依赖名称,这个不重要了
@Entry
@Component
struct Index {
@State count: number = 0;
@State timer: futimer.Timer = new futimer.Timer({
onStart: () => {
console.debug('定时器启动');
},
onCancel: () => {
console.debug('定时器取消');
},
onTask: () => {
this.count++;
console.debug(`第 ${this.count} 次执行任务`);
}
});
build() {
Column() {
Text(`现在是第 ${this.count} 秒`)
Button('开始计时')
.onClick(() => {
this.timer.start(true, 1000);
})
Button('暂停计时')
.onClick(() => {
this.timer.cancel();
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
}
}
效果图:

结语
笔者也是正在学习鸿蒙 NEXT 开发的小菜鸟一枚,希望分享的内容能对你有帮助,也欢迎给予建议,今后也会争取在这里分享更多鸿蒙 NEXT 开发相关的内容和经验。