HarmonyOS 鸿蒙Next中长时任务不生效
HarmonyOS 鸿蒙Next中长时任务不生效 【问题描述】: 长时任务不生效,我模拟了H5播放背景音乐,试了所有的模式,都不行,只要后台运行就会停止播放背景音乐
【问题现象】:没有具体报错信息,我调用的接口都是通过,没有异常,但我也不知道怎么看有没有生效
【版本信息】:开发工具,HbuilderX4.84 手机系统:HarmonyOs 6.0.0.120 SP6 Api语言版本6.0.1(21)
【复现代码】:不涉及
【尝试解决方案】:不涉及
开发者您好,运行demo,下拉手机通知栏,能看到长时任务的后台运行通知,代表长时任务生效了,只是音频未在后台播放。根据提供demo初步分析,排查出以下两个问题: 1、backgroundTaskManager.startBackgroundRunning中长时任务mode不应该用VOIP视频通话类型,应该改成AUDIO_PLAYBACK音视频播放类型,申请的长时任务类型必须与实际业务匹配。 2、注意长时任务申请时机,需要在进入后台之前就申请长时任务,而不是进入后台之后,音乐停了,再去申请长时任务。
如果修改后依然无法解决问题,请提供如下信息:详细描述修改后出现的问题。
更多关于HarmonyOS 鸿蒙Next中长时任务不生效的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
【背景知识】 ArkWeb:用于在应用程序中显示Web页面内容。 长时任务:应用退至后台后,在后台需要长时间运行用户可感知的任务,如播放音乐、导航等。为防止应用进程被挂起,导致对应功能异常,可以申请长时任务,使应用在后台长时间运行。在长时任务中,支持同时申请多种类型的任务,也可以对任务类型进行更新。应用退至后台执行业务时,系统会做一致性校验,确保应用在执行相应的长时任务。 应用在申请长时任务成功后,通知栏会显示与长时任务相关联的消息,用户删除通知栏消息时,系统会自动停止长时任务。
【解决方案】 系统为了省电和稳定,会强制终止H5的后台行为,Web组件要实现音频后台播放,必须通过“长时任务”向系统申请许可,否则用户一退到后台,音频就会停止播放。 实现步骤如下:
- 需要module.json5中配置ohos.permission.KEEP_BACKGROUND_RUNNING权限。
{
"module" : {
// To do sth.
"requestPermissions":[
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "$string:reason",
"usedScene": {
"abilities": [
"FormAbility"
],
"when":"inuse"
}
}
]
}
}
- 声明后台模式类型。 在module.json5文件中为需要使用长时任务的UIAbility声明相应的长时任务类型,配置文件中填写长时任务类型的配置项。音频、视频在后台播放需配置为audioPlayback。
"module": {
"abilities": [
{
"backgroundModes": [
// 长时任务类型的配置项
"audioPlayback"
]
}
],
// To do sth.
}
- 如果应用本身没有后台播放业务,可以通过监听生命周期函数onBackground来判断应用是否已进入后台。 在EntryAblity.ets中的回调中获取前后台的状态,通过AppStorage将状态保存,然后在Web页面通过@Watch来监听变量的变化。
export default class EntryAbility extends UIAbility {
onForeground(): void {
// 切到前台,设置isForeGround值为true
AppStorage.setOrCreate('isForeGround', true);
}
onBackground(): void {
// 切到后台,设置isForeGround值为false
AppStorage.setOrCreate('isForeGround', false)
}
}
- 在Web加载H5音频页面并创建长时任务。 注意:创建wantAgent.WantAgentInfo时添加需要被拉起应用的bundleName和abilityName。
import { webview } from '@kit.ArkWeb'
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { wantAgent, WantAgent } from '@kit.AbilityKit';
@Entry
@Component
export struct WebAudioPage {
// 通过getUIContext().getHostContext()方法,来获取page所在的UIAbility上下文
private context: Context | undefined = this.getUIContext().getHostContext();
controller: webview.WebviewController = new webview.WebviewController()
// 启动长时任务的函数(独立函数,避免this指向错误)
// 监听isForeGround的值
@Watch('network') @StorageLink('isForeGround') isForeGround: boolean = false;
network() {
// 切换到前台了
if (this.isForeGround) {
this.stopContinuousTask()
} else {
// 切换到后台了
this.startContinuousTask()
}
}
startContinuousTask() {
// 指定任务类型为audioPlayback
const taskTypes = ['audioPlayback'];
// 指定要启动的Ability(EntryAbility)
let wantAgentInfo: wantAgent.WantAgentInfo = {wants: [{
bundleName: 'com.example.xxx', // 替换为你的实际包名
abilityName: 'EntryAbility',
}],
actionType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG],
};
try {
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
backgroundTaskManager.startBackgroundRunning(this.context, taskTypes, wantAgentObj)
.then((res: backgroundTaskManager.ContinuousTaskNotification) => {
console.info('长时任务启动成功,音频将在后台持续播放');
})
.catch((error: BusinessError) => {
console.error(`启动长时任务失败: code=${error.code}, message=${error.message}`);
});
}).catch((error: BusinessError) => {
console.error('获取 WantAgent 失败:', error);
});
} catch (error) {
console.error('异常:', error);
}
}
stopContinuousTask() {
backgroundTaskManager.stopBackgroundRunning(this.context).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() {
Web({
src: $rawfile('audio.html'),
controller: this.controller
})
.domStorageAccess(true)
}
.width('100%')
.height('100%')
}
}
audio.mp3为音频资源文件,需要放在resources/rawfile目录下。 audio.html代码如下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<meta charset="UTF-8" />
<title>音频播放测试</title>
</head>
<body>
<h2>点击播放音频</h2>
<audio id="myAudio" controls autoplay loop>
<source src="audio.mp3" type="audio/mpeg">
</audio>
<script>
// 防止自动播放被拦截,建议用户交互后播放
document.getElementById('myAudio').addEventListener('click', function () {
this.play().catch(e => console.error('播放被阻止:', e));
});
</script>
</body>
</html>
可以先看下这个说明文档:
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/continuous-task
简单说明:
应用按需求申请长时任务,当应用无需在后台运行(任务结束)时,要及时主动取消长时任务,否则应用退至后台会被系统挂起。例如用户主动点击音乐暂停播放时,应用需及时取消对应的长时任务;用户再次点击音乐播放时,需重新申请长时任务。
若音频在后台播放时被打断,系统会自行检测和停止长时任务,音频重启播放时,需要再次申请长时任务。
后台播放音频的应用,在停止长时任务的同时,需要暂停或停止音频流,否则应用会被系统强制终止。
设备当前应用申请和取消长时任务示例代码如下:
function callback(info: backgroundTaskManager.ContinuousTaskCancelInfo) {
// 长时任务id
console.info('OnContinuousTaskCancel callback id ' + info.id);
// 长时任务取消原因
console.info('OnContinuousTaskCancel callback reason ' + info.reason);
}
@Entry
@Component
struct Index {
@State message: string = 'ContinuousTask';
// 通过getUIContext().getHostContext()方法,来获取page所在的UIAbility上下文
private context: Context | undefined = this.getUIContext().getHostContext();
OnContinuousTaskCancel() {
try {
backgroundTaskManager.on("continuousTaskCancel", callback);
console.info(`Succeeded in operationing OnContinuousTaskCancel.`);
} catch (error) {
console.error(`Operation OnContinuousTaskCancel failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
OffContinuousTaskCancel() {
try {
// callback参数不传,则取消所有已注册的回调
backgroundTaskManager.off("continuousTaskCancel", callback);
console.info(`Succeeded in operationing OffContinuousTaskCancel.`);
} catch (error) {
console.error(`Operation OffContinuousTaskCancel failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
// 申请长时任务.then()写法
startContinuousTask() {
let wantAgentInfo: wantAgent.WantAgentInfo = {
// 点击通知后,将要执行的动作列表
// 添加需要被拉起应用的bundleName和abilityName
wants: [
{
bundleName: "com.example.myapplication",
abilityName: "MainAbility"
}
],
// 指定点击通知栏消息后的动作是拉起ability
actionType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG],
// 车钥匙长时任务子类型,从API version 16开始支持。只有申请bluetoothInteraction类型的长时任务,车钥匙子类型才能生效。
// 确保extraInfo参数中的Key值为backgroundTaskManager.BackgroundModeType.SUB_MODE,否则子类型不生效。
// extraInfo: { [backgroundTaskManager.BackgroundModeType.SUB_MODE] : backgroundTaskManager.BackgroundSubMode.CAR_KEY }
};
try {
// 通过wantAgent模块下getWantAgent方法获取WantAgent对象
// 在元服务中,使用wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: object) => {替换下面一行代码
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
try {
let list: Array<string> = ["audioRecording"];
// let list: Array<string> = ["bluetoothInteraction"]; 长时任务类型包含bluetoothInteraction,CAR_KEY子类型合法
// 在元服务中,let list: Array<string> = ["audioPlayback"];
backgroundTaskManager.startBackgroundRunning(this.context, list, wantAgentObj).then((res: backgroundTaskManager.ContinuousTaskNotification) => {
console.info("Operation startBackgroundRunning succeeded");
// 此处执行具体的长时任务逻辑,如录音,录制等。
// 系统会对业务场景的真实性进行检测,如果没有实际执行对应的业务,系统可能会取消对应的长时任务并挂起应用。
}).catch((error: BusinessError) => {
console.error(`Failed to Operation startBackgroundRunning. code is ${error.code} message is ${error.message}`);
});
} catch (error) {
console.error(`Failed to Operation startBackgroundRunning. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
});
} catch (error) {
console.error(`Failed to Operation getWantAgent. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
// 取消长时任务.then()写法
stopContinuousTask() {
backgroundTaskManager.stopBackgroundRunning(this.context).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() {
Row() {
Column() {
Text("Index")
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button() {
Text('申请长时任务').fontSize(25).fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({ top: 10 })
.backgroundColor('#0D9FFB')
.width(250)
.height(40)
.onClick(() => {
// 通过按钮申请长时任务
this.startContinuousTask();
})
Button() {
Text('取消长时任务').fontSize(25).fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({ top: 10 })
.backgroundColor('#0D9FFB')
.width(250)
.height(40)
.onClick(() => {
// 此处结束具体的长时任务的执行
// 通过按钮取消长时任务
this.stopContinuousTask();
})
Button() {
Text('注册长时任务取消回调').fontSize(25).fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({ top: 10 })
.backgroundColor('#0D9FFB')
.width(250)
.height(40)
.onClick(() => {
// 通过按钮注册长时任务取消回调
this.OnContinuousTaskCancel();
})
Button() {
Text('取消注册长时任务取消回调').fontSize(25).fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({ top: 10 })
.backgroundColor('#0D9FFB')
.width(250)
.height(40)
.onClick(() => {
// 通过按钮取消注册长时任务取消回调
this.OffContinuousTaskCancel();
})
}
.width('100%')
}
.height('100%')
}
}
你好,若应用需申请长时任务,应用无需在后台运行(任务结束)时,要及时主动取消长时任务,否则应用退至后台会被系统挂起。例如用户主动点击音乐暂停播放时,应用需及时取消对应的长时任务;用户再次点击音乐播放时,需重新申请长时任务。
若音频在后台播放时被打断,系统会自行检测和停止长时任务,音频重启播放时,需要再次申请长时任务。
后台播放音频的应用,在停止长时任务的同时,需要暂停或停止音频流,否则应用会被系统强制终止。
关于AUDIO_PLAYBACK(音视频播放)说明:
- 音视频投播,是指将一台设备的音视频投至另一台设备播放。投播退至后台,长时任务会检测音视频播放和投屏两个业务,只要有其一正常运行,长时任务就不会终止。
- 当应用需要在后台播放媒体类型(流类型为STREAM_USAGE_MUSIC、STREAM_USAGE_MOVIE和STREAM_USAGE_AUDIOBOOK)和游戏类型(流类型为STREAM_USAGE_GAME)时,必须接入媒体会话服务(AVSession)并申请AUDIO_PLAYBACK类型长时任务。
- 除了上述播放类型,针对用户可感知的其他播放任务,如果应用需要在后台长时间运行该任务,必须申请AUDIO_PLAYBACK类型长时任务,无需接入AVSession。
- 如果应用不满足上述接入规范,退至后台播放时会被系统静音并冻结,无法在后台正常播放,直到应用重新切回前台时,才会解除静音并恢复播放。
- 从API version 20开始,申请AUDIO_PLAYBACK类型长时任务但不接入AVSession,申请长时任务成功后会在通知栏显示通知;接入AVSession后,后台任务模块不会发送通知栏通知,由AVSession发送通知。对于API version 19及之前的版本,后台任务模块不会在通知栏显示通知。
运行限制:
- 申请长时任务后,应用未执行相应的业务,系统会对应用进行管控,即应用退至后台会被挂起。如系统检测到应用申请了AUDIO_PLAYBACK(音视频播放),但实际未播放音乐。
- 申请长时任务后,应用执行的业务类型与申请的不一致,系统会对应用进行管控,即应用退至后台会被挂起。如系统检测到应用只申请了AUDIO_PLAYBACK(音视频播放),但实际上除了播放音乐(对应AUDIO_PLAYBACK类型),还在进行录制(对应AUDIO_RECORDING类型)。
- 申请长时任务后,应用的业务已执行完,系统会对应用进行管控,即应用退至后台会被挂起。
- 若运行长时任务的进程后台负载持续高于所申请类型的典型负载,系统会对应用进行管控,即应用退至后台会被挂起或终止。
说明
应用按需求申请长时任务,当应用无需在后台运行(任务结束)时,要及时主动取消长时任务,否则应用退至后台会被系统挂起。例如用户主动点击音乐暂停播放时,应用需及时取消对应的长时任务;用户再次点击音乐播放时,需重新申请长时任务。
若音频在后台播放时被打断,系统会自行检测和停止长时任务,音频重启播放时,需要再次申请长时任务。
后台播放音频的应用,在停止长时任务的同时,需要暂停或停止音频流,否则应用会被系统强制终止。
在HarmonyOS Next中,长时任务(Continuous Task)的设计和实现机制与之前的版本有显著差异,这可能是导致您遇到问题的根本原因。以下是针对您问题的具体分析:
1. 核心机制变更
HarmonyOS Next的长时任务管理更为严格,主要服务于系统级关键场景(如导航、音乐播放、录音等)。应用需要在module.json5配置文件中明确声明所需的长时任务类型,并在代码中动态申请相应的系统能力。
2. 关键排查点
- 权限与配置:请确认已在
module.json5中正确声明了ohos.permission.KEEP_BACKGROUND_RUNNING权限,并在abilities下为对应Ability配置了backgroundModes(例如,音乐播放需包含"audioPlayback")。 - 动态申请:配置完成后,必须在代码中(如
onCreate或播放开始前)调用backgroundTaskManager.requestSuspendDelay()等相关API来动态申请后台运行延迟挂起。仅靠配置无法自动生效。 - 场景匹配:系统会严格校验申请场景是否与声明的
backgroundModes匹配。H5页面播放背景音乐,需确保触发长时任务申请的代码路径与播放行为直接关联,且被系统识别为有效的音频播放场景。
3. 建议操作步骤
- 检查配置:核对
module.json5中的权限与backgroundModes声明。 - 验证API调用:确保在播放开始时有成功调用长时任务申请API,并处理其返回的
DelaySuspendInfo对象。 - 使用系统能力:对于音频播放,建议优先使用HarmonyOS的音频管理API(
@ohos.multimedia.audio)而非纯Web方式,这有助于系统准确识别任务类型。 - 查看日志:通过DevEco Studio的日志查看器,筛选
BackgroundTaskManager相关标签,可以观察长时任务的申请、授予或拒绝详情。
由于HarmonyOS Next的后台管理策略强调系统资源合理分配,非核心场景的持续后台运行会受到限制。请重点确认上述配置与动态调用环节是否已正确完成。


