后台任务:申请长时任务防止应用被挂起(38)
2026/6/18 16:35:15 网站建设 项目流程

在鸿蒙(HarmonyOS)系统中,当应用退至后台(如返回主界面、锁屏或切换应用)后,为了降低设备耗电并保障系统流畅度,系统默认会在约三秒后对应用进程进行管控(挂起或终止)。如果应用需要在后台执行用户可感知的长时任务(如音视频播放、屏幕录制、导航等),必须申请长时任务(Continuous Task)来防止进程被挂起。

以下是申请长时任务的完整开发流程与实战代码:

一、 配置文件准备(module.json5)

申请长时任务前,必须在配置文件中声明后台运行权限,并指定长时任务的类型。

"module": { // 1. 声明后台运行权限 "requestPermissions": [ { "name": "ohos.permission.KEEP_BACKGROUND_RUNNING", "reason": "$string:reason_background", // 需向用户说明后台运行的原因 "usedScene": { "abilities": ["EntryAbility"], "when": "always" } } ], "abilities": [ { "name": "EntryAbility", // 2. 声明后台模式类型(以音视频播放为例) "backgroundModes": ["audioPlayback"] } ] }

注:常见的后台模式包括audioPlayback(音视频播放)、audioRecording(录制)、location(定位导航)、bluetoothInteraction(蓝牙交互)等。

二、 核心代码实现

长时任务的生命周期应包裹住整个后台业务。在业务开始前申请,在业务结束后及时取消。

import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; import { wantAgent, WantAgent } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; @Entry @Component struct BackgroundTaskDemo { private context: Context = getContext(this); // 1. 开启长时任务 async startContinuousTask() { // 配置点击通知栏消息后的动作(通常是拉起应用主界面) let wantAgentInfo: wantAgent.WantAgentInfo = { wants: [{ bundleName: this.context.abilityInfo?.bundleName, abilityName: this.context.abilityInfo?.name }], actionType: wantAgent.OperationType.START_ABILITY, requestCode: 0, actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] }; try { const wantAgentObj: WantAgent = await wantAgent.getWantAgent(wantAgentInfo); // 申请长时任务 await backgroundTaskManager.startBackgroundRunning( this.context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, // 任务类型 wantAgentObj ); console.info('长时任务申请成功'); } catch (error) { console.error('长时任务申请失败:', (error as BusinessError).message); } } // 2. 取消长时任务 async stopContinuousTask() { try { await backgroundTaskManager.stopBackgroundRunning(this.context); console.info('长时任务已取消'); } catch (error) { console.error('长时任务取消失败:', (error as BusinessError).message); } } build() { Column({ space: 20 }) { Button('开启后台播放').onClick(() => this.startContinuousTask()) Button('停止后台播放').onClick(() => this.stopContinuousTask()) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } }

三、 核心约束与注意事项

  1. 及时主动取消:当后台业务执行完毕(如用户点击暂停、音频播放结束)时,应用必须主动调用stopBackgroundRunning取消长时任务。若不及时取消,系统会强行终止该任务。
  2. 一致性校验:系统会对长时任务进行一致性校验。如果应用申请了AUDIO_PLAYBACK但实际并未播放音频,或者实际执行的业务类型与申请的不一致,系统会管控并取消该长时任务。
  3. 音视频播放的特殊要求:若要通过AUDIO_PLAYBACK实现后台播放,应用必须接入媒体会话服务(AVSession),以支持播控中心的统一管控。
  4. 数量限制:一个 UIAbility 同一时刻仅支持申请一个长时任务。如需同时申请多个,需创建多个 UIAbility。
  5. 通知权限:申请长时任务成功后,系统会在通知栏显示关联消息。应用需确保已开启通知权限,否则用户无法感知后台任务的运行状态。

四、 多长时任务并发(API 21+ 特性)

从 API 21 开始,系统打破了单个 UIAbility 只能申请一个长时任务的限制,支持同一时刻申请最多 10 个长时任务。这在“一边导航一边播放音乐”的复合场景中非常实用。

核心代码示例:

// 申请多个不同类型的长时任务 await backgroundTaskManager.startBackgroundRunning( this.context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj ); await backgroundTaskManager.startBackgroundRunning( this.context, backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj );

五、 资源切换与防挂起机制

当应用在后台进行音视频切歌或切换资源时,不需要取消并重新申请长时任务。但必须注意系统的“防挂起”管控机制:

  1. 无缝切换:直接在播放器内切换资源,并重新设置AVSession的元数据和播控状态即可。
  2. 防挂起时间窗口:系统不允许应用创建长时任务后长时间处于“无实际业务”的状态。切歌操作必须在1分钟内重新起播,不能一直处于暂停或停止状态,否则系统会强制取消该长时任务。
import { avSession } from '@kit.AVSessionKit'; import { media } from '@kit.MediaKit'; // 假设已在类中初始化了 AVPlayer 和 AVSession private async switchAudioInBackground(newAudioUrl: string, metadata: avSession.AVMetadata) { try { // 1. 重置播放器并加载新资源(无需取消长时任务) await this.avPlayer.reset(); this.avPlayer.url = newAudioUrl; await this.avPlayer.prepare(); this.avPlayer.play(); // 2. 同步更新 AVSession 的元数据(歌曲名、歌手、封面等) await this.session.setAVMetadata(metadata); // 3. 同步更新 AVSession 的播控状态(重置进度为0,状态为播放中) const playbackState: avSession.AVPlaybackState = { state: avSession.PlaybackState.PLAYBACK_STATE_PLAY, position: { elapsedTime: 0, updateTime: new Date().getTime() }, duration: metadata.duration }; await this.session.setAVPlaybackState(playbackState); console.info('后台无缝切歌成功'); } catch (error) { console.error('后台切歌失败:', (error as BusinessError).message); } }

六、 应对系统一致性校验与强制终止

系统会对长时任务进行严格的“言行一致”校验。如果应用申请了DATA_TRANSFER(数据传输)模式,系统会持续监测网络流量。若长时间无流量或实际执行的业务与声明不符,系统会强制挂起或终止应用。

最佳实践

  • 按需申请,用完即弃:在业务真正开始(如点击播放、开始下载)时申请,在业务结束(如暂停、下载完成)时立即调用stopBackgroundRunning
  • 避免空转:绝不要在后台维持一个没有实际产出(无音频、无网络请求、无定位)的长时任务。
import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; import { BusinessError } from '@kit.BasicServicesKit'; @Entry @Component struct StrictTaskDemo { private context: Context = getContext(this); private isDownloading: boolean = false; // 1. 业务真正开始时:申请长时任务 async startDownloadTask() { if (this.isDownloading) return; try { // 配置 WantAgent 用于通知栏点击拉起应用 const wantAgentObj = await this.createWantAgent(); // 申请 DATA_TRANSFER 长时任务 await backgroundTaskManager.startBackgroundRunning( this.context, backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj ); // 申请成功后,再真正启动业务逻辑 this.isDownloading = true; this.executeRealDownloadLogic(); console.info('长时任务申请成功,开始下载'); } catch (error) { console.error('长时任务申请失败:', (error as BusinessError).message); } } // 2. 业务结束或暂停时:立即取消长时任务 async stopDownloadTask() { if (!this.isDownloading) return; try { // 先暂停真实的业务逻辑(避免空转) this.isDownloading = false; this.pauseRealDownloadLogic(); // 立即释放长时任务 await backgroundTaskManager.stopBackgroundRunning(this.context); console.info('下载已暂停,长时任务已取消'); } catch (error) { console.error('长时任务取消失败:', (error as BusinessError).message); } } // 模拟真实的下载逻辑 private executeRealDownloadLogic() { /* 实际下载代码 */ } private pauseRealDownloadLogic() { /* 实际暂停代码 */ } // 构建 WantAgent 辅助方法 private async createWantAgent() { /* 略 */ } build() { Column({ space: 20 }) { Button('开始下载').onClick(() => this.startDownloadTask()) Button('暂停下载').onClick(() => this.stopDownloadTask()) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } }

七、 非实时任务的更优解:WorkScheduler(延迟任务)

并非所有后台任务都需要“长时任务”来保持 CPU 唤醒。对于实时性要求不高、可延迟执行的维护类任务(如:Wi-Fi 环境下同步云端数据、定期清理本地缓存、上传日志),使用长时任务会显著增加设备功耗。

替代方案:WorkScheduler
通过@kit.BackgroundTasksKit中的workScheduler模块,将任务注册到系统队列,由系统根据设备状态(如“正在充电且连接 Wi-Fi”)统一调度唤醒。

核心代码示例:

import { workScheduler } from '@kit.BackgroundTasksKit'; const workInfo: workScheduler.WorkInfo = { workId: 1001, bundleName: 'com.example.app', abilityName: 'MyWorkSchedulerAbility', // 需实现 WorkSchedulerExtensionAbility networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI, // 仅在 Wi-Fi 下触发 isCharging: true, // 仅在充电时触发 repeatCycleTime: 30 * 60 * 1000 // 30分钟循环一次 }; try { workScheduler.startWork(workInfo); console.info('延迟任务注册成功'); } catch (error) { console.error(`注册失败: ${(error as BusinessError).message}`); }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询