Harmony os——长时任务(Continuous Task,ArkTS)

Harmony os------长时任务(Continuous Task,ArkTS)

一句话: 前台/后台长期跑、用户能感知的业务(音乐、导航、下载、录屏...) → 必须申请「长时任务」,否则一退后台就被挂起/静音/杀进程。


1. 长时任务干嘛用?

典型场景(用户能明显感觉到你还在干活):

  • 🎵 后台播放音乐 / 播放视频 / 投屏播放
  • 📡 导航、实时定位
  • 🎤 录音 / 录屏退后台继续录
  • 📤 后台长时间上传 / 下载
  • 🔵 蓝牙传文件、车钥匙
  • 🔗 多设备互联、分布式投屏
  • 📞 音视频通话(VoIP)
  • 🛡 PC/2in1 上的杀毒、DLP 等持续计算任务

只要你退到后台还要长时间跑 ,并且是用户可感知的业务 → 就要配套申请对应类型的长时任务。


2. 长时任务类型总览(一定要记住这张表)

类型常量 配置项字段 典型场景举例
DATA_TRANSFER dataTransfer 浏览器/网盘后台上传下载(非托管下载)
AUDIO_PLAYBACK audioPlayback 音视频播放、投播、游戏音频等(需接入 AVSession)
AUDIO_RECORDING audioRecording 录音、录屏退后台
LOCATION location 后台定位 / 导航
BLUETOOTH_INTERACTION bluetoothInteraction 蓝牙传文件、车钥匙
MULTI_DEVICE_CONNECTION multiDeviceConnection 分布式业务、多设备互联、投播(元服务也可用)
VOIP voip 音视频通话退后台
TASK_KEEPING taskKeeping 杀毒、DLP 等计算任务(PC/2in1 为主,需特权 ACL)

一些特别说明(高频坑点)

  • DATA_TRANSFER

    • 如果用「上传/下载代理接口托管给系统」,,那你申请 DATA_TRANSFER 也没用,退后台还是会挂起。

    • 如果自己实现传输逻辑 → 可以申请 DATA_TRANSFER

    • 必须定期更新传输进度(首次更新不能超过 10 分钟),否则长时任务会被系统取消。

      进度要通过实况窗通知 更新,详见 startBackgroundRunning 里的示例。

  • AUDIO_PLAYBACK

    • 媒体类型(STREAM_USAGE_MUSIC/MOVIE/AUDIOBOOK/GAME)后台播放 → 必须:

      1. 接入 AVSession
      2. 申请 AUDIO_PLAYBACK 长时任务
    • 否则:

      • 退后台会被 静音 + 冻结,只有切回前台才恢复。
    • 从 API 20 起:

      • 如果没接入 AVSession 但申请了 AUDIO_PLAYBACK → 由后台任务模块在通知栏显示通知。
      • 如果接入了 AVSession → 通知改由 AVSession 发。

3. 约束 & 系统管控逻辑

3.1 谁能申请?

  • Stage 模型 :只能在 UIAbility 里申请。

  • FA 模型 :只能在 ServiceAbility 里申请。

  • 支持:

    • 当前应用自己申请
    • 跨设备 / 跨应用申请 → 只对系统应用开放

3.2 数量限制(版本差异)

  • API 21 起

    • 一个 UIAbility 同时最多可申请 10 个长时任务
    • 用的是新版接口: startBackgroundRunning(context, request: ContinuousTaskRequest)
  • API 20 及以前

    • 一个 UIAbility / ServiceAbility 同一时刻只能有一个长时任务
    • 如果一个应用需要多个长时任务 → 得开多个 UIAbility 来申请。

但注意:只要其中一个 UIAbility 申请了长时任务,整个应用下的进程都不会被挂起。


3.3 系统何时会「动刀」管你的应用?

申请长时任务 ≠ 永久豁免,系统会做一致性校验:

  1. 申请了但不干对应的活

    • 例如:申请 AUDIO_PLAYBACK,但实际没有播放任何音频/视频。 → 应用退后台时,会被挂起。
  2. 业务类型和申请不一致

    • 只申请了 AUDIO_PLAYBACK,实际在后台还做录音(应该申请 AUDIO_RECORDING)。 → 系统判定违规,同样会挂起。
  3. 业务已经结束还不取消

    • 比如用户手动暂停音乐,你长时任务还挂着不关。 → 系统检测到业务不再发生,会挂起。
  4. 后台负载过高

    • 长时任务属于「用户可感知+资源有限」模式,系统会根据典型负载审查。
    • 如果进程长期高负载,不符合该类型合理行为 → 可能被挂起或直接终止。
  5. 后台播放音频时被打断

    • 被系统或其他事件打断时长时任务会被停。
    • 音频重新播放 → 你要重新申请长时任务。

重点:任务结束一定要主动 stop,不要挨系统的刀。


4. 通知栏 & 用户控制权

  • 申请长时任务成功后:

    • 通知栏会显示「正在运行录制任务 / 播放任务...」之类的通知。
    • 用户手动删除通知 → 系统会自动停止这条长时任务。
  • 对音频播放:

    • 停止长时任务时,你也要把音频流停掉,不然系统可能会强制终止应用

5. 关键 API 总览(backgroundTaskManager

模块: @kit.BackgroundTasksKit + @kit.AbilityKit 中的 wantAgent

5.1 旧版:一个 UIAbility 一个任务

php 复制代码
 startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent): Promise<void>;
 ​
 stopBackgroundRunning(context: Context): Promise<void>;
  • bgMode 是一个字符串或数组(如 "audioRecording""audioPlayback"...)
  • 用于 API 20 及以前的「单任务模式」。

5.2 新版:支持多个连续任务(API 21)

php 复制代码
 startBackgroundRunning(context: Context, request: ContinuousTaskRequest): Promise<ContinuousTaskNotification>;
 ​
 stopBackgroundRunning(context: Context, continuousTaskId: number): Promise<void>;
  • ContinuousTaskRequest:可以一次性配置多个类型,比如:["audioPlayback", "location"]
  • 返回 ContinuousTaskNotification,里面有 notificationId 等信息。

5.3 监听长时任务被系统取消

javascript 复制代码
 backgroundTaskManager.on("continuousTaskCancel", (info) => {
   console.info('Canceled task id: ' + info.id);
   console.info('Reason: ' + info.reason);
 });
 ​
 backgroundTaskManager.off("continuousTaskCancel", callback); // 取消监听

场景:比如被系统判断不符合规则,或用户清掉通知栏 → 会触发这个回调。


6. 录制场景示例(录音/录屏)

6.1 配置权限 & 后台模式(module.json5)

json 复制代码
 "module": {
   "abilities": [
     {
       // ... 你的 EntryAbility / MainAbility ...
       "backgroundModes": [
         "audioRecording",
         "bluetoothInteraction",
         "audioPlayback"
       ]
     }
   ]
 }

还要在工程里声明权限:ohos.permission.KEEP_BACKGROUND_RUNNING(系统权限配置处)。


6.2 关键代码拆解(then 写法)

按钮点击 → 申请录制长时任务 → 通知栏出一条"正在录制" → 点击另一按钮取消。

typescript 复制代码
 import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
 import { wantAgent, WantAgent } from '@kit.AbilityKit';
 import { BusinessError } from '@kit.BasicServicesKit';
 ​
 function continuousTaskCancelCallback(info: backgroundTaskManager.ContinuousTaskCancelInfo) {
   console.info('ContinuousTask canceled, id: ' + info.id);
   console.info('Reason: ' + info.reason);
 }
 ​
 @Entry
 @Component
 struct Index {
   @State message: string = 'ContinuousTask';
   private context: Context | undefined = this.getUIContext().getHostContext();
 ​
   // 注册任务取消回调
   OnContinuousTaskCancel() {
     try {
       backgroundTaskManager.on('continuousTaskCancel', continuousTaskCancelCallback);
     } catch (error) {
       console.error(`OnContinuousTaskCancel failed. code: ${(error as BusinessError).code}, msg: ${(error as BusinessError).message}`);
     }
   }
 ​
   // 取消注册
   OffContinuousTaskCancel() {
     try {
       backgroundTaskManager.off('continuousTaskCancel', continuousTaskCancelCallback);
     } catch (error) {
       console.error(`OffContinuousTaskCancel failed. code: ${(error as BusinessError).code}, msg: ${(error as BusinessError).message}`);
     }
   }
 ​
   // 申请长时任务(录制类)
   startContinuousTask() {
     const info: wantAgent.WantAgentInfo = {
       wants: [
         {
           bundleName: 'com.example.myapplication',
           abilityName: 'MainAbility'
         }
       ],
       actionType: wantAgent.OperationType.START_ABILITY,
       requestCode: 0,
       actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG],
     };
 ​
     wantAgent.getWantAgent(info).then((agent: WantAgent) => {
       const list: Array<string> = ['audioRecording']; // 录制长时任务
       backgroundTaskManager.startBackgroundRunning(this.context!, list, agent)
         .then(() => {
           console.info('startBackgroundRunning succeeded');
           // 这里开始真正的录制逻辑(录音/录屏等)
         })
         .catch((err: BusinessError) => {
           console.error(`startBackgroundRunning failed. code: ${err.code}, msg: ${err.message}`);
         });
     }).catch((err: BusinessError) => {
       console.error(`getWantAgent failed. code: ${err.code}, msg: ${err.message}`);
     });
   }
 ​
   // 取消长时任务
   stopContinuousTask() {
     backgroundTaskManager.stopBackgroundRunning(this.context!)
       .then(() => {
         console.info('stopBackgroundRunning succeeded');
         // 同时要停止录制逻辑
       })
       .catch((err: BusinessError) => {
         console.error(`stopBackgroundRunning failed. code: ${err.code}, msg: ${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%')
   }
 }

7. 和「短时任务」的对比小结(帮你一起记)

对比点 短时任务(requestSuspendDelay) 长时任务(startBackgroundRunning)
用途 应用即将被挂起前,争取一点点时间做收尾 退后台后长期持续跑业务(音乐 / 导航 / 下载 / 通话等)
时间尺度 单次最多几分钟,总配额 10 分钟 / 天左右 可长时间运行,只要任务真实且类型匹配
用户可感知 通常不可感知(只是你在做收尾) 用户明显能感知(播放、导航、下载、通话...)
通知栏 无固定通知要求,主要系统管理 必须有对应通知(或由 AVSession 管),用户可手动停止
典型触发点 前台或 onBackground 回调时 一般在用户开始某个长期任务时(播放按钮、开始导航等)
核心风险 超时不 cancel 会被管控 申请类型与业务不一致 / 业务已结束不 cancel 会被管控
相关推荐
抱琴_40 分钟前
大屏性能优化终极方案:请求合并+智能缓存双剑合璧
前端·javascript
fruge41 分钟前
低版本浏览器兼容方案:IE11 适配 ES6 语法与 CSS 新特性
前端·css·es6
颜酱1 小时前
开发工具链-构建、测试、代码质量校验常用包的比较
前端·javascript·node.js
颜酱2 小时前
package.json 配置指南
前端·javascript·node.js
todoitbo2 小时前
基于 DevUI MateChat 搭建前端编程学习智能助手:从痛点到解决方案
前端·学习·ai·状态模式·devui·matechat
oden2 小时前
SEO听不懂?看完这篇你明天就能优化网站了
前端
IT_陈寒2 小时前
React性能优化:这5个Hooks技巧让我减少了40%的重新渲染
前端·人工智能·后端
Sunhen_Qiletian2 小时前
《Python开发之语言基础》第六集:操作文件
前端·数据库·python
珑墨2 小时前
【唯一随机数】如何用JavaScript的Set生成唯一的随机数?
开发语言·前端·javascript·ecmascript