HarmonyOS BgTaskUtil 后台长时任务入门:让 App 在后台持续运行
文章目录
- 前言
- 一、为什么需要后台长时任务?
- 二、BgTaskUtil 是什么?
- 三、核心 API 详解
- 3.1 startBackgroundRunning
- 3.2 stopBackgroundRunning
- 四、内部实现原理
- 五、权限配置
- 六、完整 Demo 演示
- 6.1 数据结构定义
- 6.2 组件状态
- 6.3 获取选中的模式
- 6.4 启动长时任务
- 6.5 停止长时任务
- 七、运行效果
- 八、注意事项
- 九、小结
前言
近期发现一款很有意思的HarmonyOS 三方库, 地址 @pura/harmony-utils(V1.4.0) , 作者是"桃花镇童长老", 我这里也是直接通过该作者公布的源码进行案例编写进行,写了到目前写了一部分demo ,感觉确实很有帮助,这里呢也是开始写一个系列的演示demo 供大家参考。如有帮助可以在OpenHarmony中进行下载安装进行使用哦
案例demo导航展示
↓↓↓↓↓↓接下来言归正传 ↓↓↓↓
一、为什么需要后台长时任务?
我们在日常开发 App 时,经常会遇到这样的需求:
- 用户切换到后台,音乐播放器依然要继续播放歌曲
- 导航 App 在后台持续更新定位信息
- 下载工具在后台继续下载大文件
- VoIP 电话 App 保持通话连接不断线
这些场景有一个共同点——应用切入后台后,业务逻辑必须继续运行。
在 HarmonyOS 中,系统为了节省电量,会对后台应用进行资源回收。如果不申请"长时任务",应用切到后台一段时间后业务就会被系统暂停。
长时任务(Continuous Task):允许有实际后台业务需要(如音视频播放、定位等)的应用在后台持续运行,系统会在通知栏显示一条持续通知,告知用户该应用正在后台活动。
二、BgTaskUtil 是什么?
BgTaskUtil是对 HarmonyOS@kit.BackgroundTasksKit中backgroundTaskManager模块的封装,将底层繁琐的 API 调用简化为两个核心方法:
// 申请长时任务staticasyncstartBackgroundRunning(request:TaskRequest):Promise<backgroundTaskManager.ContinuousTaskNotification>// 取消长时任务staticasyncstopBackgroundRunning(context?:Context):Promise<void>使用BgTaskUtil,你不需要手动构造 WantAgent、转换任务模式字符串——这些繁琐工作都由工具类内部完成。
三、核心 API 详解
3.1 startBackgroundRunning
staticasyncstartBackgroundRunning(request:TaskRequest):Promise<backgroundTaskManager.ContinuousTaskNotification>参数说明:
TaskRequest接口定义:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
backgroundTaskModes | BackgroundTaskMode[] | 是 | 后台任务模式数组,可多选,最多 10 个 |
backgroundTaskSubmodes | BackgroundTaskSubMode[] | 否 | 子模式(API21+) |
combinedTaskNotification | boolean | 否 | 是否合并通知,默认 false |
continuousTaskId | number | 否 | 任务 ID(API21+),-1 表示新建 |
context | Context | 否 | 上下文,默认使用AppUtil.getContext() |
wantAgentInfo | wantAgent.WantAgentInfo | 否 | 点击通知后的行为,默认跳转 EntryAbility |
返回值:
ContinuousTaskNotification对象,包含:
continuousTaskId:系统分配的长时任务唯一 ID
异常(BusinessError):
| 错误码 | 说明 |
|---|---|
201 | 权限不足,需声明ohos.permission.KEEP_BACKGROUND_RUNNING |
16000007 | 服务忙,存在并发任务冲突,稍后重试 |
16000151 | WantAgent 配置无效 |
9800005 | 长时任务校验失败 |
9800006 | 通知校验失败 |
3.2 stopBackgroundRunning
staticasyncstopBackgroundRunning(context?:Context):Promise<void>取消已申请的长时任务,通知栏的持续通知也会随之消失。context可选,默认使用全局 Context。
四、内部实现原理
BgTaskUtil内部的startBackgroundRunning做了三件事:
- 获取 Context:优先使用传入的 context,否则调用
AppUtil.getContext()获取全局上下文 - 转换任务模式:将枚举值
BackgroundTaskMode转为系统识别的字符串(如MODE_AUDIO_PLAYBACK→"audioPlayback") - 构造 WantAgent:创建默认的 WantAgent(点击通知后跳转 EntryAbility),再调用底层 API
staticasyncstartBackgroundRunning(request:TaskRequest):Promise<backgroundTaskManager.ContinuousTaskNotification>{constcontext:Context=request?.context??AppUtil.getContext();constbgModes:string[]=BgTaskUtil.convertTaskModes(request.backgroundTaskModes);constwantAgentObj:WantAgent=awaitBgTaskUtil.getDefaultWantAgent(request?.wantAgentInfo);consttaskNotification=awaitbackgroundTaskManager.startBackgroundRunning(context,bgModes,wantAgentObj);returntaskNotification;}五、权限配置
在module.json5中声明权限:
{ "module": { "requestPermissions": [ { "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" } ] } }注意:
KEEP_BACKGROUND_RUNNING是 restricted 权限,需要向华为申请授权,普通开发者测试时可能无法在模拟器上使用,建议使用真机测试。
六、完整 Demo 演示
以下代码来自BgTaskDemoPage.ets,展示了完整的启动/停止长时任务流程:
效果演示
6.1 数据结构定义
import{BgTaskUtil}from'../Utils/BgTaskUtil';import{TaskRequest}from'../Utils/entity/TaskRequest';import{backgroundTaskManager}from'@kit.BackgroundTasksKit';import{ToastUtil}from'../Utils/action/ToastUtil';import{BusinessError}from'@kit.BasicServicesKit';interfaceBgModeItem{label:string;mode:backgroundTaskManager.BackgroundTaskMode;desc:string;selected:boolean;}6.2 组件状态
@Entry@Componentstruct BgTaskDemoPage{@Statemodes:BgModeItem[]=[{label:'📡 数据传输',mode:backgroundTaskManager.BackgroundTaskMode.MODE_DATA_TRANSFER,desc:'下载/上传',selected:false},{label:'🎵 音视频播放',mode:backgroundTaskManager.BackgroundTaskMode.MODE_AUDIO_PLAYBACK,desc:'后台音乐',selected:false},{label:'🎙️ 录音录屏',mode:backgroundTaskManager.BackgroundTaskMode.MODE_AUDIO_RECORDING,desc:'后台录制',selected:false},{label:'📍 定位导航',mode:backgroundTaskManager.BackgroundTaskMode.MODE_LOCATION,desc:'导航定位',selected:false},{label:'🔵 蓝牙业务',mode:backgroundTaskManager.BackgroundTaskMode.MODE_BLUETOOTH_INTERACTION,desc:'蓝牙数据传输',selected:false},{label:'🔗 多设备互联',mode:backgroundTaskManager.BackgroundTaskMode.MODE_MULTI_DEVICE_CONNECTION,desc:'设备发现/连接',selected:false},{label:'📞 音视频通话',mode:backgroundTaskManager.BackgroundTaskMode.MODE_VOIP,desc:'VoIP通话',selected:false},{label:'⚙️ 计算任务',mode:backgroundTaskManager.BackgroundTaskMode.MODE_TASK_KEEPING,desc:'后台计算',selected:true},{label:'🎬 多媒体业务',mode:backgroundTaskManager.BackgroundTaskMode.MODE_AV_PLAYBACK_AND_RECORD,desc:'音视频处理',selected:false},{label:'⚡ 特殊场景',mode:backgroundTaskManager.BackgroundTaskMode.MODE_SPECIAL_SCENARIO_PROCESSING,desc:'特殊场景',selected:false},];@StatetaskStatus:'idle'|'running'|'stopping'='idle';@StatetaskId:string='';@StatecanStart:boolean=false;6.3 获取选中的模式
getSelectedModes():backgroundTaskManager.BackgroundTaskMode[]{constselected:backgroundTaskManager.BackgroundTaskMode[]=[];for(leti=0;i<this.modes.length;i++){if(this.modes[i].selected){selected.push(this.modes[i].mode);}}returnselected;}6.4 启动长时任务
asyncstartTask(){if(this.taskStatus!=='idle'){return;}constselectedModes=this.getSelectedModes();if(selectedModes.length===0){this.addLog('⚠️','请至少选择一种后台任务模式','warn');return;}this.taskStatus='running';this.taskId='';this.addLog('🚀',`申请长时任务... 模式:${this.getModeNames(selectedModes)}`,'info');this.addLog('📌',`共${selectedModes.length}种模式`,'info');constrequest:TaskRequest={backgroundTaskModes:selectedModes,combinedTaskNotification:false,};try{constnotification=awaitBgTaskUtil.startBackgroundRunning(request);this.taskId=String(notification.continuousTaskId);this.taskStatus='running';this.addLog('✅',`长时任务启动成功! TaskId:${this.taskId}`,'success');ToastUtil.showShort('后台任务已启动');}catch(e){consterr=easBusinessError;this.taskStatus='idle';lethint='';if(err.code===201){hint='权限不足,需要 KEEP_BACKGROUND_RUNNING 权限';}elseif(err.code===16000007){hint='服务忙,并发任务冲突,请稍后重试';}elseif(err.code===16000151){hint='WantAgent 配置无效';}else{hint=`错误码:${err.code}`;}this.addLog('❌',`启动失败:${hint}`,'error');ToastUtil.showShort('启动失败: '+hint);}}6.5 停止长时任务
asyncstopTask(){if(this.taskStatus==='idle'||this.taskStatus==='stopping'){return;}this.taskStatus='stopping';this.addLog('⏹️','正在取消长时任务...','info');try{awaitBgTaskUtil.stopBackgroundRunning();this.addLog('✅','长时任务已取消','success');this.taskStatus='idle';this.taskId='';ToastUtil.showShort('后台任务已停止');}catch(e){consterr=easBusinessError;this.taskStatus='idle';this.addLog('❌',`取消失败: 错误码${err.code}`,'error');ToastUtil.showShort('停止失败: '+err.code);}}七、运行效果
页面加载后,底部日志区域会出现:
[初始化] 后台任务演示页面已加载 [说明] 模拟器/部分设备可能不支持,请使用真机测试选择"⚙️ 计算任务"模式后,点击"启动长时任务":
[🚀] 申请长时任务... 模式: TASK_KEEPING [📌] 共 1 种模式 [✅] 长时任务启动成功! TaskId: 12345通知栏会出现系统持续通知,提示后台任务正在运行。点击"停止任务":
[⏹️] 正在取消长时任务... [✅] 长时任务已取消八、注意事项
- 必须使用真机测试:模拟器不支持长时任务
- 权限需要华为审核:
KEEP_BACKGROUND_RUNNING是 restricted 权限,正式上架需申请 - 模式需与业务匹配:选择的
BackgroundTaskMode必须与实际业务一致,系统会校验 - 一个 UIAbility 最多 10 个并发长时任务:超出会报
9800005错误 - 停止任务要及时:业务完成后必须调用
stopBackgroundRunning,避免浪费系统资源
九、小结
| 方法 | 说明 |
|---|---|
startBackgroundRunning(request) | 申请长时任务,返回任务通知对象(含 taskId) |
stopBackgroundRunning(context?) | 取消长时任务,通知栏通知同步消失 |
BgTaskUtil将后台任务的申请流程封装为简单的两步操作,开发者只需关心业务逻辑:选择模式 → 构造 TaskRequest → 调用 startBackgroundRunning,不需要深入了解底层 WantAgent 和任务模式字符串的转换细节。
