功耗优化在鸿蒙移动应用开发中占据着至关重要的地位,它直接关系到用户体验、设备续航以及应用在后台的运行效率。鸿蒙系统作为华为推出的全场景分布式操作系统,在功耗管理方面提供了丰富的特性和API支持。
在开发过程中,我们发现功耗热点主要集中在以下几方面:
- 后台任务:不合理的使用后台任务会导致设备无法进入休眠状态
- 网络请求:频繁的网络活动尤其是移动数据使用会显著增加功耗
- 定位服务:持续高精度的GPS定位会快速耗尽电池
- 屏幕与图形:不合理的UI刷新策略和动画效果会增加GPU负担
- 传感器使用:未及时释放的传感器监听会持续消耗电量
那么针对以上几点,一起来详细探讨下如何进行性能优化叭:
一、后台任务(长时任务和短时任务)
1.短时任务的合理使用
短时任务(Short-time Task)适用于应用退至后台后需要临时完成一些耗时较短的操作,如小文件下载、缓存处理或信息发送等。
以下是一个申请短时任务执行计算的demo
typescript
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
import { util } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
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)
}
}
从性能分析数据来看,短时任务执行阶段应用主线程的平均CPU占用率为12.6%,最高达到40.0%;而任务取消后未被挂起阶段的平均CPU占用率降至2.2%,最高28.6%。这表明短时任务确实会显著增加系统资源消耗,因此总结出以下经验:
- 最小化任务时长:尽可能缩短短时任务的执行时间,避免长时间占用系统资源
- 避免并发任务:同时运行多个短时任务会导致CPU负载过高,可能引起前台应用卡顿
- 及时释放资源:任务完成后应立即取消短时任务申请,减少不必要的能耗
- 异常处理:添加适当的错误处理逻辑,确保任务异常时也能正确释放资源
2.长时任务的适用场景与优化
长时任务(Long-time Task)适用于需要在后台持续运行用户可感知的功能,如音乐播放、导航、录音等。与短时任务不同,长时任务允许应用在后台长时间运行,但需要声明具体的任务类型,并且系统会进行严格的权限校验。
根据官方文档给出的长时任务使用场景,写了一个音视频播放长时任务的demo:
typescript
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common, wantAgent } from '@kit.AbilityKit';
@Entry
@Component
struct MusicPlayer {
private longTaskId: number = 0;
// 申请音视频播放长时任务
requestContinuousTask() {
try {
backgroundTaskManager.startBackgroundRunning(getContext(),
backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK,{
// 点击通知后,将要执行的动作列表
// 添加需要被拉起应用的bundleName和abilityName
wants: [
{
bundleName: (getContext() as common.UIAbilityContext).abilityInfo.bundleName,
abilityName: "EntryAbility"
}
],
// 指定点击通知栏消息后的动作是拉起ability
actionType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG],
}).then(() => {
console.info('Audio playback long-term task requested');
this.startMusicPlayback();
}).catch((err: BusinessError) => {
console.error(`Failed to request audio playback task. Code: ${err.code}, message: ${err.message}`);
});
} catch (error) {
console.error(`Exception when requesting audio playback task. Code: ${(error as BusinessError).code}, message: ${(error as BusinessError).message}`);
}
}
// 取消长时任务
stopContinuousTask() {
try {
backgroundTaskManager.stopBackgroundRunning(getContext()).then(() => {
console.info('Long-term task stopped');
this.stopMusicPlayback();
}).catch((err: BusinessError) => {
console.error(`取消长时任务失败 Code: ${err.code}, message: ${err.message}`);
});
} catch (error) {
console.error(`取消长时任务出现异常 Code: ${(error as BusinessError).code}, message: ${(error as BusinessError).message}`);
}
}
// 模拟音乐播放开始
startMusicPlayback() {
console.info('音乐后台播放');
// 实际音乐播放逻辑...
}
// 模拟音乐播放停止
stopMusicPlayback() {
console.info('音乐后台播放停止');
// 实际音乐停止逻辑...
}
build() {
Column() {
Button('开始音乐后台播放')
.onClick(() => this.requestContinuousTask())
Button('停止音乐后台播放')
.onClick(() => this.stopContinuousTask())
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
对于长时任务,我总结出以下优化点:
- 按需申请:仅在确实需要时才申请长时任务,任务完成后立即释放
- 类型匹配:准确声明任务类型,避免滥用通用类型
- 资源释放:任务停止时释放所有相关资源,包括媒体播放器、定位服务等
- 适配系统限制:不同任务类型可能有特殊要求,如音视频播放必须使用媒体会话服务
- 用户感知:对于录音等涉及隐私的任务,必须提供明确的用户提示和授权机制
针对后台任务优化还有其他各个方面,如线程池与任务调度、延迟任务等,欢迎各位小伙伴一起讨论