赞
踩
设备返回主界面、锁屏、应用切换等操作会使应用退至后台。为了降低设备耗电速度、保障用户使用流畅度,系统会对退至后台的应用进行管控,包括进程挂起和进程终止。为了保障后台音乐播放、日历提醒等功能的正常使用,系统提供了受规范约束的后台任务,扩展应用在后台的运行时间。
本文将介绍各类后台任务的基本概念和适用场景,并且通过对短时任务和长时任务两个场景的性能分析说明合理运行后台任务的必要性。
应用退至后台一小段时间后,应用进程会被挂起,无法执行对应的任务。如果应用在后台仍需要执行耗时不长的任务,可以申请短时任务,扩展应用在后台的运行时间。
短时任务适用于小文件下载、缓存、信息发送等实时性高、需要临时占用资源执行的任务。详细的开发指导可参考短时任务。
下面代码在申请短时任务后执行了一个耗时计算任务。
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) } }
使用 IDE 中的 Time Profiler 获取示例应用从开始计算任务并退到后台执行一分钟内的性能数据。获取到的数据如下图。
图1 短时任务 Time Profiler 泳道图
从上图中可以看出,Native Callstack 泳道与 H:computeTask 相对应的时间段内应用进程处于活跃状态,CPU 占用率在较高范围内变化。任务取消后,应用仍然处于运行状态,但是进程的活跃程度和 CPU 占用率都明显下降,直到在几秒后系统将应用挂起,不再占用 CPU。
分别框选任务执行阶段和任务取消后未被挂起阶段对应的 Native Callstack 如下图,查看应用主线程在两个阶段的平均 CPU 占用率和最高 CPU 占用率情况。
图2 任务执行阶段的 CPU 占用率
图3 任务取消后未被挂起阶段的 CPU 占用率
可以看到应用主线程在任务执行阶段的平均 CPU 占用率为 12.6%,最高 CPU 占用率为 40.0%,在任务取消后未被挂起阶段的平均 CPU 占用率为 2.2%,最高 CPU 占用率为 28.6%。
在后台运行短时任务,会占用系统 CPU,在后台执行过多的短时任务就有可能会导致前台的应用卡顿,因此建议非必要情况不使用短时任务,使用时也避免同时申请过多的短时任务。
应用退至后台后,在后台需要长时间运行用户可感知的任务,如播放音乐、导航等。为防止应用进程被挂起,导致对应功能异常,可以申请长时任务,使应用在后台长时间运行。申请长时任务后,系统会做相应的校验,确保应用在执行相应的长时任务。
长时任务支持的类型包括数据传输、音视频播放、录音、定位导航、蓝牙相关、多设备互联、WLAN 相关、音视频通话、计算任务。可以根据下表的场景举例选择相应的长时任务类型。
下面模拟一个后台定位的场景。应用订阅设备位置变化,每隔一秒获取位置信息,为了保证应用在退到后台后仍然可以使用定位服务,申请了定位类型的长时任务。
首先需要在 module.json5 配置文件中为需要使用长时任务的 EntryAbility 声明任务类型。
{
"module": {
...
"abilities": [
{
"name": "EntryAbility",
...
"backgroundModes": [
"location"
]
}
],
}
}
需要使用到的相关权限如下:
权限申请方式参考配置文件权限申明,在 module.json5 中进行配置。其中部分权限申请以及打开使能通知开关需要用户手动确认。系统为申请的长时任务发布通知栏消息时,应用的使能通知开关必须处于开启状态,否则用户无法感知后台正在运行的长时任务。
后台定位的实现代码如下:
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) } }
基于上述场景,使用功耗测试工具获取 30min 设备功耗,得到的数据如下表。
对比后台存在长时定位任务和不存在长时任务时的功耗数据,当后台存在定位任务持续运行时,设备在 30 分钟内的功耗明显增加。
从功耗角度考虑,应用应该避免过多使用长时任务,针对必须使用长时任务的场景,也可以优化任务执行过程,减少设备功耗。以下是一些优化建议:
应用退至后台后,如果需要执行实时性要求不高的任务,可以使用延迟任务。当应用满足设定条件(包括网络类型、充电类型、存储状态、电池状态、定时状态等)时,将任务添加到执行队列,系统会根据内存、功耗、设备温度、用户使用习惯等统一调度拉起应用。
应用退到后台或进程终止后,仍然有一些提醒用户的定时类任务,例如购物类应用抢购提醒等,为满足此类功能场景,系统提供了代理提醒(reminderAgentManager)的能力。当应用退至后台或进程终止后,系统会代理应用做相应的提醒。详细的开发指导可参考代理提醒。
当前支持的提醒类型包括:
合理的选择和使用后台任务对于优化用户体验,减少性能消耗非常重要。以下表格对比总结了各类后台任务的概念、适用场景以及任务执行过程中的应用状态。
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.基本概念
2.构建第一个ArkTS应用
3.……
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
https://docs.qq.com/doc/DZVVkRGRUd3pHSnFG
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。