概览:本文介绍了后台应用的功耗管理,涵盖后台任务、硬件资源和软件资源的使用规范。包括后台任务的特性、不同类型任务的应用场景,以及后台硬件和软件资源(如蓝牙、网络、音频等)的合理使用和功耗优化建议。
什么是后台任务功耗?
后台任务:是指那些不需要与用户直接交互,或者仅在用户不可见的情况下运行的任务,具有非交互性、低优先级、资源占用较少等特点。
后台任务功耗:指应用在用户不可见且未与其交互时的功耗,与前台任务相比,后台任务通常不需要处理大量的UI更新和事件,因此功耗相对较低。然而,后台任务仍然需要执行一些必要的操作,如数据同步、状态更新等,这些操作也会消耗一定的资源。
应用后台运行的设计与实现
| 任务类型 | 概念 | 应用退至后台状态 | 适用场景 |
|---|---|---|---|
| 无后台 | 不执行任务,直接退到后台。 | 应用在几秒内挂起。 | 适用于大多数场景。即用户无持续使用需求、任务,具有明确终点、无需后台数据处理的应用,如计算器、手电筒、相册等。 |
| 短时任务 | 实时性高、耗时不长的任务。 | 在单次配额内,应用不会被挂起,直到取消任务。配额超时不取消,应用进程会被终止。 | 小文件下载、缓存、信息发送等任务需要临时占用资源执行。 |
| 长时任务 | 长时间运行、用户可感知的任务。 | 应用不会被挂起,直到取消任务。任务结束不取消,应用进程会被终止。 | 数据传输、音频播放、录音、定位导航、蓝牙、WLAN 相关、多设备互联、音视频通话、计算任务。 |
| 延迟任务 | 实时性不高、可延迟执行的任务,满足条件后放入执行队列,系统统一调度。 | 应用退到后台时挂起,满足条件后系统统一调度拉起应用,创建Extension进程执行任务。单次回调最长运行2分钟,超时不取消,系统终止Extension进程。 | 软件更新、信息收集、数据处理等。 |
| 代理提醒 | 系统代理应用提醒。 | 应用挂起或进程终止,满足条件后系统代理应用提醒。 | 闹钟、倒计时、日历。 |
案例:后台申请短时任务
短时任务概念:在应用进行小文件下载、缓存、信息发送等业务场景时,如果应用短暂退至后台导致 进程被挂起,重新切换到前台可能会出现异常状态。此时,可以申请短时任务作为解决方案。以下示例展 示了如何使用ApplicationContext订阅应用前后台切换的回调,以在应用切后台时申请短时任务,解决因 短暂切换前后台导致的消息发送异常问题。
1、系统息屏场景/应用置于后台场景:前台应用在自动息屏后,会被识别为置于后台。此时,应用可以申请短时任务,默认剩余时长上限为三分钟。
2、当短时任务的剩余时间不足时,系统会触发回调,停止任务。
module.json5
"requestPermissions": [
{
// 保持应用在后台持续运行(用于长/短时任务退后台,允许Service Ability在后台持续运行)
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "$string:permission_keep_background_running_desc",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
],
resources > base > element > string.json
{
"name": "permission_keep_background_running_desc",
"value": "It is used to backtrack long/short tasks, allowing Service Ability to run continuously in the background"
},
ets > pages > Index.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
import suspendTaskUtils from '../utils/SuspendTaskUtils';
import { ShortTermTaskModel } from '../viewModel/ShortTermTaskModel';
@Entry
@Component
struct Index {
private shortTermTaskModel: ShortTermTaskModel = new ShortTermTaskModel();
private messageCount: number = 0;
private taskTimer = 0;
aboutToDisappear(): void {
clearInterval(this.taskTimer);
suspendTaskUtils.cancelSuspendDelay(this.shortTermTaskModel.suspendTaskInfo.id);
this.shortTermTaskModel.offAppStateChange();
}
build() {
Column({ space: 20 }) {
Button('开启应用程序状态监听,切换到后台时申请短时任务')
.width('95%')
.onClick(() => {
this.shortTermTaskModel.subscribeStateChange();
this.taskTimer = setInterval(() => {
this.messageCount++;
hilog.info(0x0000, 'short_term_task', `already sent :${this.messageCount} messages`);
}, 2000);
})
Button('获取应用程序进入挂起状态前的剩余时间')
.width('95%')
.onClick(async () => {
try {
let delayTime = await suspendTaskUtils.getRemainingDelayTime(this.shortTermTaskModel.suspendTaskInfo.id);
this.getUIContext().getPromptAction().showToast({
message: '应用程序进入挂起状态前的剩余时间:' + delayTime
});
} catch (err) {
this.getUIContext().getPromptAction().showToast({
message: '获取剩余时间失败,请先将应用置于后台,随后再次返回此页面,重新点击该按钮'
});
}
})
Button('取消短时任务')
.width('95%')
.onClick(() => {
suspendTaskUtils.cancelSuspendDelay(this.shortTermTaskModel.suspendTaskInfo.id);
})
Button('关闭应用程序状态监听')
.width('95%')
.onClick(() => {
clearInterval(this.taskTimer);
this.shortTermTaskModel.offAppStateChange();
})
}
.height('100%')
.width('100%')
}
}
entryability > EntryAbility.ets > onCreate
AppStorage.setOrCreate('context', this.context);
ets > viewModel > ShortTermTaskModel.ets
import { ApplicationStateChangeCallback, common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import suspendTaskUtils from '../utils/SuspendTaskUtils';
import { SuspendTaskInfo } from './SuspendTaskInfo';
const TAG: string = 'short_term_task';
export class ShortTermTaskModel {
public suspendTaskInfo: SuspendTaskInfo = { id: 0, delayTime: 0 };
private context: common.UIAbilityContext = AppStorage.get("context") as common.UIAbilityContext;
// 开启应用程序状态监听
subscribeStateChange() {
let that = this;
let applicationContext = this.context.getApplicationContext();
let applicationStateChangeCallback: ApplicationStateChangeCallback = {
onApplicationForeground() {
hilog.info(0x0000, TAG, 'applicationStateChangeCallback onApplicationForeground');
},
onApplicationBackground() {
hilog.info(0x0000, TAG, 'applicationStateChangeCallback onApplicationBackground');
// 当应用从前台切换到后台时申请短时任务
that.suspendTaskInfo = suspendTaskUtils.requestSuspendDelay('Suspend Task');
hilog.info(0x0000, TAG,
`requestSuspendDelay, id:${that.suspendTaskInfo.id}, delayTime:${that.suspendTaskInfo.delayTime}`);
}
}
try {
applicationContext.on('applicationStateChange', applicationStateChangeCallback);
} catch (paramError) {
hilog.error(0x0000, TAG,
`error: ${(paramError as BusinessError).code}, ${(paramError as BusinessError).message}`);
}
}
// 关闭应用程序状态监听
offAppStateChange(): void {
let applicationContext = this.context.getApplicationContext();
try {
applicationContext.off('applicationStateChange');
hilog.info(0x0000, TAG, 'ApplicationStateChange off succeeded.');
} catch (paramError) {
hilog.error(0x0000, TAG,
`error: ${(paramError as BusinessError).code}, ${(paramError as BusinessError).message}`);
}
}
}
ets > utils > SuspendTaskUtils.ets
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { SuspendTaskInfo } from '../viewModel/SuspendTaskInfo';
const TAG: string = 'short_term_task';
/**
* 挂起任务工具类
*/
class SuspendTaskUtils {
/**
* 申请短时任务
* @param reason 设置延迟任务暂停原因
* @returns
*/
requestSuspendDelay(reason: string): SuspendTaskInfo {
let id: number; // 短时任务 ID
let delayTime: number; // 应用程序进入挂起状态前的剩余时间
try {
// 请求短时任务
let delayInfo = backgroundTaskManager.requestSuspendDelay(reason,() => {
// 此函数用于在应用程序请求的短时任务即将超时时回调应用程序
hilog.info(0x0000, TAG, `Request suspension delay will time out.`);
backgroundTaskManager.cancelSuspendDelay(delayInfo.requestId);
})
id = delayInfo.requestId;
delayTime = delayInfo.actualDelayTime;
let taskInfo = {
id: id,
delayTime: delayTime
} as SuspendTaskInfo;
return taskInfo;
} catch (err) {
let taskInfo = {
id: 0,
delayTime: 0
} as SuspendTaskInfo;
return taskInfo;
}
}
/**
* 获取短时任务的剩余时间
* @param id 短时任务ID
* @returns
*/
async getRemainingDelayTime(id: number): Promise<number> {
let delayTime: number = -1;
await backgroundTaskManager.getRemainingDelayTime(id).then((res: number) => {
delayTime = res;
hilog.info(0x0000, TAG, 'Operation getRemainingDelayTime succeeded. Data: ' + JSON.stringify(res));
}).catch((err: BusinessError) => {
hilog.error(0x0000, TAG, 'Operation getRemainingDelayTime failed. Cause: ' + err.code);
});
return delayTime;
}
/**
* 取消短时任务
* @param id 短时任务ID
* @returns
*/
cancelSuspendDelay(id: number): boolean {
try {
backgroundTaskManager.cancelSuspendDelay(id);
hilog.info(0x0000, TAG, 'cancelSuspendDelay succeeded.');
} catch (err) {
hilog.error(0x0000, TAG, `cancelSuspendDelay failed. Cause: ${JSON.stringify(err)}`);
return false;
}
return true;
}
}
let suspendTaskUtils = new SuspendTaskUtils();
export default suspendTaskUtils as SuspendTaskUtils;
ets > viewModel > SuspendTaskInfo.ets
export interface SuspendTaskInfo {
id: number; // 短时任务 ID
delayTime: number; // 应用程序进入挂起状态前的剩余时间
}
案例:后台申请长时任务
长时任务概念:当应用涉及数据传输、音频播放、录音操作、定位导航、蓝牙和WLAN相关应用、多 设备互联、音视频通话、复杂计算任务等场景时,需要应用在后台长时间运行。为了确保应用在这些情况下 正常运作,可以申请后台长时任务来实现。以下示例展示了如何使用长时任务管理应用的定位服务,以实现 应用在后台长时间运行时,持续获取设备位置信息的功能。
1.使用定位应用场景,应用前台运行时开启位置订阅,控制台定期打印位置信息,说明长时任务执行。
2.应用退至后台持续运行,控制台日志还会定时打印位置信息,说明任务仍被执行。
module.json5
"abilities": [
{
"name": "EntryAbility",
// 用于定义应用在后台运行时可以执行的任务类型,location表示允许应用在后台获取位置信息
"backgroundModes": [
'location'
]
}
],
"requestPermissions": [
{
// 获取精准定位(用于长时任务页面,允许应用获取设备位置信息)
"name": "ohos.permission.LOCATION",
"reason": "$string:permission_location_desc",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
},
},
{
// 用于后台定位的场景(用于长时任务页面,允许应用在后台运行时获取设备位置信息)
"name": "ohos.permission.LOCATION_IN_BACKGROUND",
"reason": "$string:permission_location_background_desc",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
},
},
{
// 获取模糊定位(用于长时任务页面,允许应用获取设备模糊位置信息)
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:permission_approximately_location_desc",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
},
},
{
// 保持应用在后台持续运行(用于长/短时任务退后台,允许Service Ability在后台持续运行)
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "$string:permission_keep_background_running_desc",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
},
}
]
resources > base > element > string.json
{
"name": "permission_location_desc",
"value": "Used for long task pages to allow applications to obtain device location information"
},
{
"name": "permission_location_background_desc",
"value": "Used for long task pages to allow applications to obtain device location information while running in the background"
},
{
"name": "permission_approximately_location_desc",
"value": "Used for long task pages to allow applications to get fuzzy device location information"
},
{
"name": "permission_keep_background_running_desc",
"value": "It is used to backtrack long/short tasks, allowing Service Ability to run continuously in the background"
}
ets > pages > Index.ets
private longTermTaskModel: LongTermTaskModel = new LongTermTaskModel();
aboutToDisappear(): void {
geoLocationManager.off('locationChange');
this.longTermTaskModel.stopLongTask();
}
Button('申请定位权限')
.onClick(() => {
this.longTermTaskModel.requestPermissionsFromUser();
})
Button('开启定位服务')
.onClick(() => {
this.longTermTaskModel.startLongTask();
this.longTermTaskModel.getLocation();
})
Button('关闭定位服务')
.onClick(async () => {
geoLocationManager.off('locationChange');
this.longTermTaskModel.stopLongTask();
})
ets > viewModel > LongTermTaskModel.ets
import { abilityAccessCtrl, common, PermissionRequestResult, Permissions, wantAgent, WantAgent } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { geoLocationManager } from '@kit.LocationKit';
const TAG: string = 'long_term_task';
export class LongTermTaskModel {
private context: common.UIAbilityContext = AppStorage.get("context") as common.UIAbilityContext;
// 申请与位置相关的权限
requestPermissionsFromUser(): void {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let permissionList: Permissions[] = [
'ohos.permission.INTERNET', // 允许应用访问网络权限
'ohos.permission.LOCATION', // 精准定位权限
'ohos.permission.APPROXIMATELY_LOCATION' // 模糊定位权限
];
atManager.requestPermissionsFromUser(this.context, permissionList)
.then((data: PermissionRequestResult) => {
hilog.info(0x0000, TAG, `data: ${JSON.stringify(data)}`);
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG, `requestPermissionsFromUser fail: ${JSON.stringify(err)}`);
});
}
// 位置信息回调
locationCallback = async (location: geoLocationManager.Location) => {
hilog.info(0x0000, TAG, `locationCallback: data: ${JSON.stringify(location)}`);
};
// 获取位置
async getLocation() {
let request: geoLocationManager.LocationRequest = {
priority: geoLocationManager.LocationRequestPriority.FIRST_FIX, // 快速获取位置优先
scenario: geoLocationManager.LocationRequestScenario.UNSET, // 表示场景信息。当scenario取值为UNSET时,priority参数生效
timeInterval: 1, // 上报位置信息的时间间隔
distanceInterval: 0, // 上报位置信息的距离间隔
maxAccuracy: 100 // 应用向系统请求位置信息时要求的精度值
};
try {
// 开启订阅请求,当位置发生改变时再次发起定位请求
geoLocationManager.on('locationChange', request, this.locationCallback);
} catch (err) {
hilog.error(0x0000, TAG, `errCode: ${JSON.stringify(err)}`);
}
}
// 开启长时任务
startLongTask(): void {
let wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
bundleName: this.context.abilityInfo.bundleName,
abilityName: this.context.abilityInfo.name
}
],
actionType: wantAgent.OperationType.START_ABILITY,
requestCode: 0,
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
try {
// wantAgent对象通过WantAgent模块中的getWantAgent方法获取
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
// 应用退至后台需持续运行时,通过该接口申请长时任务
backgroundTaskManager.startBackgroundRunning(this.context,
backgroundTaskManager.BackgroundMode.LOCATION,wantAgentObj).then(() => {
hilog.info(0x0000, TAG, `Operation startBackgroundRunning succeeded`);
})
.catch((error:BusinessError) => {
hilog.error(0x0000, TAG, `Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
})
});
} catch (error) {
hilog.error(0x0000, TAG, `Operation getWantAgent failed. error is ${JSON.stringify(error)} `);
}
}
// 停止长时任务
stopLongTask(): void {
// 停止长时任务
backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
hilog.info(0x0000, TAG, `Operation stopBackgroundRunning succeeded`);
}).catch((error: BusinessError) => {
hilog.error(0x0000, TAG, `Operation stopBackgroundRunning failed. error is ${JSON.stringify(error)} `);
});
}
}
后台硬件资源合理使用汇总
控制后台进程CPU使用率:developer.huawei.com/consumer/cn…
蓝牙资源合理使用:developer.huawei.com/consumer/cn…
网络资源合理使用:developer.huawei.com/consumer/cn…
音频资源合理使用:developer.huawei.com/consumer/cn…
GPS资源合理使用:developer.huawei.com/consumer/cn…
传感器资源合理使用:developer.huawei.com/consumer/cn…
后台软件资源合理使用汇总
后台上传下载合理使用:developer.huawei.com/consumer/cn…
后台音频播放合理使用:developer.huawei.com/consumer/cn…
后台定位导航服务合理使用:developer.huawei.com/consumer/cn…
后台系统资源合理使用:developer.huawei.com/consumer/cn…
后台场景应用功耗体验建议汇总
后台任务使用:developer.huawei.com/consumer/cn…
后台硬件资源使用:developer.huawei.com/consumer/cn…
后台软件资源使用:developer.huawei.com/consumer/cn…