一、Background Tasks Kit简介
1.1 功能介绍
设备返回主界面、锁屏、应用切换等操作会使应用退至后台。应用退至后台后,如果继续活动,可能会造成设备耗电快、用户界面卡顿等现象。为了降低设备耗电速度、保障用户使用流畅度,系统会对退至后台的应用进行管控,包括进程挂起和进程终止。典型场景包括:应用退至后台一段时间应用进程会被挂起、资源不足时系统会终止部分应用进程(即回收该进程的所有资源)。挂起后,应用进程无法使用软件资源(如公共事件、定时器等)和硬件资源(CPU、网络、GPS、蓝牙等)。
为了保障后台音乐播放、日历提醒等功能的正常使用,Background Tasks Kit提供了规范内受约束的后台任务,扩展应用在后台的运行时间。
1.2 约束与限制
资源使用约束:对于运行的进程,系统会给予一定的资源配额约束,包括进程在连续一段时间内内存的使用、CPU使用占比,以及24小时磁盘写的IO量,均有对应的配额上限。超过配额上限时,如果进程处于前台,系统会有对应的warning日志,如果进程处于后台,系统会终止该进程。
1.3 后台任务类型
Background Tasks Kit提供了规范内受约束的后台任务,包括短时任务、长时任务、延迟任务、代理提醒。
开发者可以根据如下的功能介绍,选择合适的后台任务,以满足应用退至后台后继续运行的需求。
- 短时任务:适用于实时性要求高、耗时不长的任务,例如状态保存。
- 长时任务:适用于长时间运行在后台、用户可感知的任务,例如后台播放音乐、导航、设备连接等,使用长时任务避免应用进程被挂起。
- 延迟任务:对于实时性要求不高、可延迟执行的任务,系统提供了延迟任务,即满足条件的应用退至后台后被放入执行队列,系统会根据内存、功耗等统一调度。
- 代理提醒:代理提醒是指应用退后台或进程终止后,系统会代理应用做相应的提醒。适用于定时提醒类业务,当前支持的提醒类型包括倒计时、日历和闹钟三类。
后台任务
说明 系统仅支持规范内受约束的后台任务。应用退至后台后,若未使用规范内的后台任务或选择的后台任务类型不正确,对应的应用进程会被挂起或终止。应用申请了规范内的后台任务,仅会提升应用进程被回收的优先级。当系统资源严重不足时,即使应用进程申请了规范内的后台任务,系统仍会终止部分进程,用以保障系统稳定性。
二、短时任务(ArkTS)
2.1 概述
应用退至后台一小段时间后,应用进程会被挂起,无法执行对应的任务。如果应用在后台仍需要执行耗时不长的任务,如状态保存等,可以通过本文申请短时任务,扩展应用在后台的运行时间。
2.2 约束与限制
- 申请时机:应用需要在前台或onBackground回调内,申请短时任务,否则会申请失败。
- 数量限制:一个应用同一时刻最多申请3个短时任务。以图1为例,在①②③时间段内的任意时刻,应用申请了2个短时任务;在④时间段内的任意时刻,应用申请了1个短时任务。
- 配额机制:一个应用会有一定的短时任务配额(根据系统状态和用户习惯调整),单日(24小时内)配额默认为10分钟,单次配额最大为3分钟,低电量时单次配额默认为1分钟,配额消耗完后不允许再申请短时任务。同时,系统提供获取对应短时任务剩余时间的查询接口,用以查询本次短时任务剩余时间,以确认是否继续运行其他业务。
- 配额计算:仅当应用在后台时,对应用下的短时任务计时;同一个应用下的同一个时间段的短时任务,不重复计时。以下图为例:应用有两个短时任务A和B,在前台时申请短时任务A,应用退至后台后开始计时为①,应用进入前台②后不计时,再次进入后台③后开始计时,短时任务A结束后,由于阶段④仍然有短时任务B,所以该阶段继续计时。因此,在这个过程中,该应用短时任务总耗时为①+③+④。
短时任务配额计算原理图
说明 任务完成后,应用需主动取消短时任务,否则会影响应用当日短时任务的剩余配额。- 超时:短时任务即将超时时,系统会回调应用,应用需要取消短时任务。如果超时不取消,系统会终止对应的应用进程。
2.3 接口说明
表1 主要接口
以下是短时任务开发使用的主要接口,更多接口及使用方式请见后台任务管理。
| 接口名 | 描述 |
|---|---|
| requestSuspendDelay(reason: string, callback: Callback): DelaySuspendInfo | 申请短时任务。 |
| getRemainingDelayTime(requestId: number): Promise | 获取对应短时任务的剩余时间。 |
| cancelSuspendDelay(requestId: number): void | 取消短时任务。 |
2.4 开发步骤
- 导入模块。
bash
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
- 申请短时任务并实现回调。此处回调在短时任务即将结束时触发,与应用的业务功能不耦合,短时任务申请成功后,正常执行应用本身的任务。
bash
let id: number; // 申请短时任务ID
let delayTime: number; // 本次申请短时任务的剩余时间
// 申请短时任务
function requestSuspendDelay() {
let myReason = 'test requestSuspendDelay'; // 申请原因
try {
let delayInfo = backgroundTaskManager.requestSuspendDelay(myReason, () => {
// 回调函数。应用申请的短时任务即将超时,通过此函数回调应用,执行一些清理和标注工作,并取消短时任务
console.info('suspend delay task will timeout');
try {
backgroundTaskManager.cancelSuspendDelay(id);
} catch (error) {
console.error(`Operation cancelSuspendDelay failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
})
id = delayInfo.requestId;
delayTime = delayInfo.actualDelayTime;
} catch (error) {
console.error(`Operation requestSuspendDelay failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
// 执行应用本身业务
- 获取短时任务剩余时间。查询本次短时任务的剩余时间,用以判断是否继续运行其他业务,例如应用有两个小任务,在执行完第一个小任务后,可以判断本次短时任务是否还有剩余时间来决定是否执行第二个小任务。
bash
let id: number; // 申请短时任务ID
async function getRemainingDelayTime() {
backgroundTaskManager.getRemainingDelayTime(id).then((res: number) => {
console.info('Succeeded in getting remaining delay time.');
}).catch((err: BusinessError) => {
console.error(`Failed to get remaining delay time. Code: ${err.code}, message: ${err.message}`);
})
}
- 取消短时任务。
bash
let id: number; // 申请短时任务ID
function cancelSuspendDelay() {
try {
backgroundTaskManager.cancelSuspendDelay(id);
} catch (error) {
console.error(`Operation cancelSuspendDelay failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
2.5 完整示例
申请短时任务效果图
获取短时任务剩余时间效果图
取消短时任务
示例完整代码
bash
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
let id: number; // 申请短时任务ID
let delayTime: number; // 本次申请短时任务的剩余时间
/**
* 申请短时任务
*/
function requestSuspendDelay() {
let myReason = '测试 申请短时任务'; // 申请原因
try {
let delayInfo = backgroundTaskManager.requestSuspendDelay(myReason, () => {
// 回调函数。应用申请的短时任务即将超时,通过此函数回调应用,执行一些清理和标注工作,并取消短时任务
console.info('应用申请的短时任务即将超时');
try {
backgroundTaskManager.cancelSuspendDelay(id);
console.log("取消短时任务 成功 id = ", id);
} catch (error) {
console.error(`取消短时任务 失败. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
})
id = delayInfo.requestId;
delayTime = delayInfo.actualDelayTime;
console.log("申请短时任务 成功 id = " + id + " , delayTime = " + delayTime);
} catch (error) {
console.error(`申请短时任务 失败. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
/**
* 获取短时任务剩余时间
*/
async function getRemainingDelayTime() {
backgroundTaskManager.getRemainingDelayTime(id).then((res: number) => {
console.info('获取短时任务剩余时间 成功 , res : ', res);
}).catch((err: BusinessError) => {
console.error(`获取短时任务剩余时间 失败. Code: ${err.code}, message: ${err.message}`);
})
}
@Entry
@Component
struct TestBackgroundTasksKit {
@State message: string = '申请短时任务';
build() {
Column({ space: 30 }) {
Button(this.message)
.fontSize($r('app.float.page_text_font_20fp'))
.fontWeight(FontWeight.Bold)
.onClick(() => {
requestSuspendDelay()
})
.margin({ top: 60 })
Button("获取短时任务剩余时间")
.fontSize($r('app.float.page_text_font_20fp'))
.fontWeight(FontWeight.Bold)
.onClick(() => {
getRemainingDelayTime()
})
}
.height('100%')
.width('100%')
}
}




