鸿蒙实现后台任务管理
目录:
- 1、后台任务类型
- 2、应用后台运行
- 3、后台任务类型实现
- 3.1、短时任务
- 3.2、长时任务
- 3.3、延时任务
- 3.4、能效资源
1、后台任务类型
OpenHarmony将后台任务分为四种类型,并提供了一个资源申请的扩展功能:
- 无后台业务 :应用或业务模块退到后台后,无任务需要处理。
- 短时任务:应用或业务模块退到后台后,如果有紧急不可推迟且短时间能完成的任务,如应用退后台要进行数据压缩,不可中断,则使用短时任务申请延迟进入挂起(Suspend)状态。
- 长时任务:如果是用户发起的可感知业务需要长时间后台运行,如后台播放音乐、导航、设备连接、VoIP等,则使用长时任务避免进入挂起(Suspend)状态。
- 延迟任务:延迟任务调度给应用提供一个机制,允许应用根据系统安排,在系统空闲时执行实时性不高的任务。当满足设定条件的时候,任务会被放入待调度队列,当系统空闲时调度该任务。
- 能效资源 :能效资源包括CPU资源、WORK_SCHEDULER资源、软件资源(COMMON_EVENT,TIMER)、硬件资源(GPS,BLUETOOTH)。如果应用或者进程申请了能效资源,那么根据能效资源的类型会拥有相应的特权,例如申请了CPU资源的可以不被挂起,申请了WORK_SCHEDULER后延时任务可以拥有更长的执行时间。
2、应用后台运行
minimize方法提供该能力。若主窗口调用,可以将窗口最小化,并支持在Dock栏中还原。若子窗口调用,可以将窗口隐藏。
参考代码如下:
在EntryAbility.ets的onWindowStageCreate回调中全局保存windowStage:
AppStorage.setOrCreate('context', windowStage);
在页面中获取windowStage并调用方法实现最小化:
import { window } from '@kit.ArkUI';
@Component
export struct BackgroundExecution {
@State message: string = '后台运行';
build() {
Column() {
Button(this.message)
.width('40%')
.onClick(() => {
let windowStage = AppStorage.get('context') as window.WindowStage;
windowStage.getMainWindowSync().minimize();
//当应用进入后台时就可以对接我们后面的流程了,后台任务管理,如短时任务逻辑等
//这里实现后台任务逻辑
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
3、后台任务类型实现
3.1、短时任务
短时任务适用于小文件下载、缓存、信息发送等实时性高、需要临时占用资源执行的任务。
下面代码模拟在申请短时任务后执行了一个耗时计算任务:
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import { BusinessError } from '@ohos.base';
import util from '@ohos.util';
import hiTraceMeter from '@ohos.hiTraceMeter';
const totalTimes: number = 50000000; // 循环次数
const calculateResult: string = 'Total time costed = %s ms.'; // 文本格式
@Entry
@Component
struct Index {
@State message: string = 'Click button to calculate.';
private requestId: number = 0;
// 申请短时任务
requestSuspendDelay() {
try {
let delayInfo = backgroundTaskManager.requestSuspendDelay('compute', () => {
console.info('Request suspension delay will time out.');
// 任务即将超时,取消短时任务
this.cancelSuspendDelay();
})
this.requestId = delayInfo.requestId;
} catch (error) {
console.error(`requestSuspendDelay failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
// 取消短时任务
cancelSuspendDelay() {
backgroundTaskManager.cancelSuspendDelay(this.requestId);
console.info('Request suspension delay cancel.');
}
// 计算任务
computeTask(times: number): number {
let start: number = new Date().getTime();
let a: number = 1;
let b: number = 1;
let c: number = 1;
for (let i: number = 0; i < times; i++) {
a = a * Math.random() + b * Math.random() + c * Math.random();
b = a * Math.random() + b * Math.random() + c * Math.random();
c = a * Math.random() + b * Math.random() + c * Math.random();
}
let end: number = new Date().getTime();
return end - start;
}
// 点击回调
clickCallback = () => {
this.requestSuspendDelay();
hiTraceMeter.startTrace('computeTask', 0); // 开启性能打点
let timeCost = this.computeTask(totalTimes);
this.message = util.format(calculateResult, timeCost.toString());
hiTraceMeter.finishTrace('computeTask', 0); // 结束性能打点
this.cancelSuspendDelay();
}
build() {
Column() {
Row(){
Text(this.message)
}
Row() {
Button('开始计算')
.onClick(this.clickCallback)
}
.width('100%')
.justifyContent(FlexAlign.Center)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
3.2、长时任务
下面模拟一个后台定位的场景。应用订阅设备位置变化,每隔一秒获取位置信息,为了保证应用在退到后台后仍然可以使用定位服务,申请了定位类型的长时任务
首先需要在 module.json5 配置文件中为需要使用长时任务的 EntryAbility 声明任务类型。
{
"module": {
...
"abilities": [
{
"name": "EntryAbility",
...
"backgroundModes": [
"location"
]
}
],
}
}
需要使用到的相关权限如下:
- ohos.permission.INTERNET
- ohos.permission.LOCATION_IN_BACKGROUND
- ohos.permission.APPROXIMATELY_LOCATION
- ohos.permission.LOCATION
- ohos.permission.KEEP_BACKGROUND_RUNNING
后台定位的实现代码如下:
import wantAgent, { WantAgent } from '@ohos.app.ability.wantAgent';
import common from '@ohos.app.ability.common';
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import { BusinessError } from '@ohos.base';
import geolocation from '@ohos.geoLocationManager';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import notificationManager from '@ohos.notificationManager';
const TAG: string = 'BackgroundLocation';
@Entry
@Component
export struct LongTermTaskView {
@State latitude: number = 0;
@State longitude: number = 0;
aboutToAppear() {
// 请求发送通知的许可
notificationManager.requestEnableNotification().then(() => {
console.info(`[EntryAbility] requestEnableNotification success`);
// 申请定位相关权限
let atManager = abilityAccessCtrl.createAtManager();
try {
atManager.requestPermissionsFromUser(getContext(this), [
'ohos.permission.INTERNET',
'ohos.permission.LOCATION',
'ohos.permission.LOCATION_IN_BACKGROUND',
'ohos.permission.APPROXIMATELY_LOCATION'])
.then((data) => {
console.info(`[EntryAbility], data: ${JSON.stringify(data)}`);
})
.catch((err: BusinessError) => {
console.info(`[EntryAbility], err: ${JSON.stringify(err)}`);
})
} catch (err) {
console.info(`[EntryAbility], catch err->${JSON.stringify(err)}`);
}
}).catch((err: BusinessError) => {
console.error(`[EntryAbility] requestEnableNotification failed, code is ${err.code}, message is ${err.message}`);
});
}
// 位置变化回调
locationChange = async (location: geolocation.Location) => {
console.info(TAG, `locationChange location =${JSON.stringify(location)}`);
this.latitude = location.latitude;
this.longitude = location.longitude;
}
// 获取定位
async getLocation() {
console.info(TAG, `enter getLocation`);
let requestInfo: geolocation.LocationRequest = {
priority: geolocation.LocationRequestPriority.FIRST_FIX, // 快速获取位置优先
scenario: geolocation.LocationRequestScenario.UNSET, // 未设置场景信息
timeInterval: 1, // 上报位置信息的时间间隔
distanceInterval: 0, // 上报位置信息的距离间隔
maxAccuracy: 100 // 精度信息
};
console.info(TAG, `on locationChange before`);
geolocation.on('locationChange', requestInfo, this.locationChange);
console.info(TAG, `on locationChange end`);
}
// 开始长时任务
startContinuousTask() {
let context: Context = getContext(this);
// 通知参数,指定点击长时任务通知后跳转的应用
let wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
bundleName: (context as common.UIAbilityContext).abilityInfo.bundleName,
abilityName: (context as common.UIAbilityContext).abilityInfo.name
}
],
operationType: wantAgent.OperationType.START_ABILITY,
requestCode: 0,
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
backgroundTaskManager.startBackgroundRunning(context,
backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj).then(() => {
console.info(`Succeeded in operationing startBackgroundRunning.`);
}).catch((err: BusinessError) => {
console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
});
}
// 停止长时任务
stopContinuousTask() {
backgroundTaskManager.stopBackgroundRunning(getContext()).then(() => {
console.info(`Succeeded in operationing stopBackgroundRunning.`);
}).catch((err: BusinessError) => {
console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
}
build() {
Column() {
Column() {
Text(this.latitude.toString())
Text(this.longitude.toString())
}
.width('100%')
Column() {
Button('开启定位服务')
.onClick(() => {
this.startContinuousTask();
this.getLocation();
})
Button('关闭定位服务')
.onClick(async () => {
await geolocation.off('locationChange');
this.stopContinuousTask();
})
.margin({ top: 10 })
}
.width('100%')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
3.3、延时任务
实现延迟任务回调拓展能力:
import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility';
import workScheduler from '@ohos.resourceschedule.workScheduler';
export default class MyWorkSchedulerExtensionAbility extends WorkSchedulerExtensionAbility {
// 延迟任务开始回调
onWorkStart(workInfo: workScheduler.WorkInfo) {
console.info(`onWorkStart, workInfo = ${JSON.stringify(workInfo)}`);
}
// 延迟任务结束回调
onWorkStop(workInfo: workScheduler.WorkInfo) {
console.info(`onWorkStop, workInfo is ${JSON.stringify(workInfo)}`);
}
}
{
"module": {
"extensionAbilities": [
{
"name": "MyWorkSchedulerExtensionAbility",
"srcEntry": "./ets/WorkSchedulerExtension/WorkSchedulerExtension.ets",
"label": "$string:WorkSchedulerExtensionAbility_label",
"description": "$string:WorkSchedulerExtensionAbility_desc",
"type": "workScheduler"
}
]
}
}
实现延迟任务调度:
import workScheduler from '@ohos.resourceschedule.workScheduler';
import { BusinessError } from '@ohos.base';
// 创建workinfo
const workInfo: workScheduler.WorkInfo = {
workId: 1,
networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI,
bundleName: 'com.example.application',
abilityName: 'MyWorkSchedulerExtensionAbility'
}
try {
workScheduler.startWork(workInfo);
console.info(`startWork success`);
} catch (error) {
console.error(`startWork failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
// 创建workinfo
const workInfo: workScheduler.WorkInfo = {
workId: 1,
networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI,
bundleName: 'com.example.application',
abilityName: 'MyWorkSchedulerExtensionAbility'
}
try {
workScheduler.stopWork(workInfo);
console.info(`stopWork success`);
} catch (error) {
console.error(`stopWork failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
3.4、能效资源
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
// 申请能效资源
let request = {
resourceTypes: backgroundTaskManager.ResourceType.COMMON_EVENT|
backgroundTaskManager.ResourceType.TIMER,
isApply: true,
timeOut: 0,
reason: "apply",
isPersist: true,
isProcess: true,
};
let res;
try {
res = backgroundTaskManager.applyEfficiencyResources(request);
console.info("the result of request is: " + res);
} catch (error) {
console.error(`Operation applyEfficiencyResources failed. code is ${error.code} message is ${error.message}`);
}
// 释放部分资源
request = {
resourceTypes: backgroundTaskManager.ResourceType.COMMON_EVENT,
isApply: false,
timeOut: 0,
reason: "reset",
isPersist: true,
isProcess: true,
};
try {
res = backgroundTaskManager.applyEfficiencyResources(request);
console.info("the result of request is: " + res);
} catch (error) {
console.error(`Operation applyEfficiencyResources failed. code is ${error.code} message is ${error.message}`);
}
// 释放全部资源
try {
backgroundTaskManager.resetAllEfficiencyResources();
} catch (error) {
console.error(`Operation resetAllEfficiencyResources failed. code is ${error.code} message is ${error.message}`);
}
// 使用示例也是一样
clickCallback = () => {
backgroundTaskManager.applyEfficiencyResources(request);//申请能效资源
hiTraceMeter.startTrace('computeTask', 0); // 开启性能打点
let timeCost = this.computeTask(totalTimes);//进行任务逻辑处理
this.message = util.format(calculateResult, timeCost.toString());
hiTraceMeter.finishTrace('computeTask', 0); // 结束性能打点
//任务逻辑处理完释放资源,下面是释放部分资源和释放全部资源,这里根据业务二选一即可
backgroundTaskManager.applyEfficiencyResources(request);
backgroundTaskManager.resetAllEfficiencyResources();
}