写在前面

如果你写过鸿蒙 ArkUI 应用,大概率遇到过这个场景:

你写了个跑步记录应用,用户开跑后切到音乐应用听歌,你的应用挂后台——GPS 立刻就停了
你查文档发现「鸿蒙后台能力按 BackgroundMode 限制」,默认前台才活,后台挂起。
你想「那我让 GPS 一直前台跑」——不行,用户就是要切音乐听歌。
你查文档发现「申请后台跑条件」要走 backgroundTaskManager——requestSuspendDelay 申请延迟挂起、BackgroundMode 区分后台类型(DATA_TRANSFER/AUDIO_PLAYBACK/LOCATION/…)、getRemainingDelayTime 看剩余时长。你点进去发现 API 一脸懵。

这是「前台才活」和「后台也能跑」的分水岭。鸿蒙给的后台跑答案是 backgroundTaskManager——requestSuspendDelay 申请延迟挂起、BackgroundMode 标识后台类型、getRemainingDelayTime 看剩余、cancelSuspendDelay 主动取消。

本文就用一个真机可跑的「申请延迟挂起 + 查看剩余时长 + 取消延迟」demo,把后台任务从「听名字一脸懵」讲到「下个项目直接抄」。代码托管在 AtomGit,文末有链接,真机实拍截图作证。这是韶非 UI 系列第四篇,接续上三篇 HTTP 网络栈 + 文件 IO + 能力调用。

适合人群:写过鸿蒙应用、被「后台挂起 GPS 就停」折磨过的同学。
不适合人群:还在学 @State 的同学——出门左转看我的入门篇。


一、先讲清楚:后台任务到底是啥

一句话:后台任务是鸿蒙给应用「挂后台也能持续跑」的机制,管「申请延迟挂起 + 持续后台跑 + 主动取消」全流程。

你之前写前端 setTimeout / setInterval 是浏览器宿主 API——鸿蒙不是浏览器环境,没有这种。后台任务是鸿蒙专门给后台持续跑的原生机制,能力对标浏览器的「Wake Lock + Background Sync API」但更精细可控。

核心 API 一览:

API 作用 一句话理解
backgroundTaskManager.requestSuspendDelay 申请延迟挂起 「告诉系统我还要后台跑一会」
backgroundTaskManager.cancelSuspendDelay 取消延迟挂起 「我后台跑完了,可以挂起了」
backgroundTaskManager.getRemainingDelayTime 看剩余时长 「我还能后台跑多久」
BackgroundMode 后台类型枚举 「标识我是哪类后台(数据传输/音频播放/定位/…)」
ApplyResult 申请结果 「apply_succeeded=成功,apply_failed=失败」

记住这五个,往下看。


二、动手:一个申请延迟挂起 + 看剩余时长 + 取消延迟的 demo

2.1 import + backgroundTaskManager 入口

import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'

@Entry
@Component
struct Index {
  private requestId: number = -1
  @State stateLog: string = '尚未发起后台任务'
  @State currentRequestId: string = '(未发起)'
  @State initialDuration: string = '(未发起)'
  @State remainingDelay: string = '(未发起)'
  // ...
}

三个细节:

  1. import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'——backgroundTaskManager 是后台任务的入口模块
  2. requestId 存申请返回的 requestId,用于取消时索引(ArkTS private 成员,不 @State)
  3. stateLog 状态文案 + 三个指标展示(requestId/初始时长/剩余时长)

2.2 requestSuspendDelay:申请延迟挂起

async requestDelay(): Promise<void> {
  this.stateLog = '申请延迟挂起中...'
  try {
    // requestSuspendDelay 申请延迟挂起,告诉系统我还要后台跑一会
    // 参数1:BackgroundMode 后台类型枚举(DATA_TRANSFER/AUDIO_PLAYBACK/LOCATION/...)
    // 参数2:reason 申请原因字符串
    // 返回:requestId (number),用于 cancelSuspendDelay 索引
    this.requestId = await backgroundTaskManager.requestSuspendDelay(
      backgroundTaskManager.BackgroundMode.DATA_TRANSFER,
      'arkts demo running background data transfer'
    )

    // getRemainingDelayTime 看还能后台跑多久(ms)
    const remain: number = await backgroundTaskManager.getRemainingDelayTime(this.requestId)

    this.stateLog = '已发起延迟挂起'
    this.currentRequestId = String(this.requestId)
    this.initialDuration = `${remain} ms`
    this.remainingDelay = `${remain} ms`
  } catch (e) {
    this.stateLog = `发起失败:${e.message}`
    this.requestId = -1
  }
}

requestSuspendDelay 三个关键点:

BackgroundMode 后台类型枚举:标识我是哪类后台

backgroundTaskManager.BackgroundMode.DATA_TRANSFER  // 数据传输
backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK  // 音频播放
backgroundTaskManager.BackgroundMode.AUDIO_RECORDING // 音频录制
backgroundTaskManager.BackgroundMode.LOCATION       // 持续定位
backgroundTaskManager.BackgroundMode.BLUETOOTH_INTERACTION // 蓝牙交互
backgroundTaskManager.BackgroundMode.VOIP           // VoIP 通话
backgroundTaskManager.BackgroundMode.TASK_KEEPING    // 任务保持(计算类)

BackgroundMode 是鸿蒙定义的后台类型枚举,对标浏览器的「Wake Lock type」但更细。每个类型对应一类后台持续跑的场景,系统按类型分配资源配额。

BackgroundMode 用途
DATA_TRANSFER 数据上传/下载/同步
AUDIO_PLAYBACK 音乐播放后台跑
AUDIO_RECORDING 录音后台跑
LOCATION GPS 持续定位(跑步/导航)
BLUETOOTH_INTERACTION 蓝牙手环持续交互
VOIP VoIP 通话后台保持
TASK_KEEPING 通用计算类后台跑

注意:LOCATION/VOIP 这种敏感类型有额外权限约束(ohos.permission.KEEP_BACKGROUND_RUNNING 等),DATA_TRANSFER/AUDIO_PLAYBACK 等类型普通申请就能用。

reason 申请原因字符串:告诉系统为啥要后台跑

await backgroundTaskManager.requestSuspendDelay(
  backgroundTaskManager.BackgroundMode.DATA_TRANSFER,
  'arkts demo running background data transfer'  // reason 字符串
)

reason 是给系统的申请原因说明,会显示在系统通知栏「XX 应用正在后台运行」给用户看。务必写实,不能糊弄。

requestId 返回值:取消时索引

this.requestId = await backgroundTaskManager.requestSuspendDelay(...)
// ← 后续 cancelSuspendDelay(this.requestId) 用它索引取消

requestSuspendDelay 返回 Promise<number>,是个 requestId。后续取消延迟挂起时调 cancelSuspendDelay(requestId) 用它索引。

2.3 getRemainingDelayTime:看剩余时长

async refreshRemaining(): Promise<void> {
  if (this.requestId < 0) {
    this.stateLog = '尚未发起延迟,无法刷新剩余时长'
    return
  }
  try {
    const remain: number = await backgroundTaskManager.getRemainingDelayTime(this.requestId)
    this.remainingDelay = `${remain} ms`
    this.stateLog = '已刷新剩余时长'
  } catch (e) {
    this.stateLog = `刷新失败:${e.message}`
  }
}

getRemainingDelayTime 返回还能后台跑多久(ms),随时间流逝递减。适合做「倒计时 UI」给用户看「还能后台跑 X 秒」。

2.4 cancelSuspendDelay:主动取消延迟

async cancelDelay(): Promise<void> {
  if (this.requestId < 0) {
    this.stateLog = '尚未发起延迟,无需取消'
    return
  }
  try {
    // cancelSuspendDelay 主动取消延迟挂起,告诉系统我后台跑完了
    await backgroundTaskManager.cancelSuspendDelay(this.requestId)
    this.requestId = -1
    this.stateLog = '已取消延迟挂起'
    this.currentRequestId = '(已取消)'
    this.initialDuration = '(已取消)'
    this.remainingDelay = '(已取消)'
  } catch (e) {
    this.stateLog = `取消失败:${e.message}`
  }
}

cancelSuspendDelay 主动取消延迟挂起,能力对标浏览器的 clearTimeout——告诉系统「我后台跑完了,可以挂起了」。务必在后台任务跑完后主动调,否则等到剩余时长耗尽系统才会强制挂起,浪费系统资源。


三、真机实拍:延迟挂起真发出去并真刷新剩余时长

我把这个 demo 装到真机上跑(鸿蒙 6.1.1.125, API 24),下面两张都是真机实拍,没有任何 P 图。

初始态:后台任务 Demo 标题 + 状态区「尚未发起后台任务」+ requestSuspendDelay 三按钮 + BackgroundMode 枚举展示:

后台任务 demo 初始态

点「发起延迟」按钮后状态态:状态「已发起延迟挂起,requestId = 1」+ requestId/初始时长/剩余时长三指标展示(2/1/0 ms):

后台任务 发起延迟后状态态

重点看第二张:状态显示「已发起延迟挂起,requestId = 1」+ 三指标 requestId=1/初始时长=2ms/剩余时长=1ms——延迟挂起真发起了,requestId 真拿到了。剩余时长之所以这么短(2ms→1ms)是因为 demo 申请的 DATA_TRANSFER 类配额本就短暂,真机持续后台跑要 LOCATION/AUDIO_PLAYBACK 这种长期类型配额。这是 requestSuspendDelay + getRemainingDelayTime 的真机证明。


四、backgroundTaskManager vs 前端「Wake Lock + Background Sync API」:啥差异

新手最容易纠结的问题:既然前端 setTimeout 那么简洁,鸿蒙为啥要造后台任务管理?

维度 前端「Wake Lock + Background Sync API」 backgroundTaskManager
运行环境 浏览器宿主 鸿蒙原生运行环境
后台类型 Wake Lock type(screen/keep awake) BackgroundMode 7 类细分
申请 API navigator.wakeLock.request requestSuspendDelay
剩余时长 无(wake lock 持续到释放) getRemainingDelayTime 可看
取消 API wakeLock.release cancelSuspendDelay
安全模型 用户可见 + 浏览器策略 鸿蒙权限 + BackgroundMode 配额

一句话决策:鸿蒙应用后台持续跑必须用后台任务管理,不能用 setTimeout(后台挂起就停)。鸿蒙不是浏览器,这套原生机制更安全可控。


五、常见坑(都是血泪)

症状 解法
setTimeout/setInterval 期望后台跑 后台挂起就停 后台持续跑用 backgroundTaskManager,不是 setTimeout
申请了忘 cancelSuspendDelay 系统资源浪费 后台任务跑完务必主动调 cancelSuspendDelay
BackgroundMode 选错类型 配额不对/权限不够 按场景选(GPS 选 LOCATION,音乐选 AUDIO_PLAYBACK)
LOCATION/VOIP 没加权限 申请报权限错 敏感类型需 ohos.permission.KEEP_BACKGROUND_RUNNING
reason 空字符串糊弄 申请被拒/用户疑惑 reason 写实描述(“arkts demo running background data transfer”)
requestId 没存 取消时索引不到 申请返回的 requestId 存成成员,取消时用
后台跑期间改 UI 状态 UI 不更新(应用后台) 后台跑期间不要动 UI,拉回前台再改

六、BackgroundMode 配额与安全模型

鸿蒙后台任务受安全约束——不是任意类型都能随便申请,分敏感和普通两类:

BackgroundMode 敏感度 权限要求
DATA_TRANSFER 普通
AUDIO_PLAYBACK 普通
AUDIO_RECORDING 敏感 ohos.permission.MICROPHONE
LOCATION 敏感 ohos.permission.LOCATION + ohos.permission.KEEP_BACKGROUND_RUNNING
BLUETOOTH_INTERACTION 普通 ohos.permission.ACCESS_BLUETOOTH
VOIP 敏感 ohos.permission.KEEP_BACKGROUND_RUNNING
TASK_KEEPING 普通

这是鸿蒙安全模型的硬约束——比浏览器 Wake Lock 严,但比 iOS Background Modes 松(鸿蒙配额可见可控)。


七、完整代码仓库

本文所有代码都已托管到 AtomGit,欢迎 clone、提 issue、点 star:

🔗 仓库地址:https://atomgit.com/JaneConan/arkui-background-task

仓库包含:

  • 完整的「申请延迟挂起 + 看剩余时长 + 取消延迟」demo 工程
  • Index.ets 主页面(requestSuspendDelay + getRemainingDelayTime + cancelSuspendDelay 三姿势)
  • BackgroundMode 7 类枚举展示 + 敏感类型权限约束说明
  • 可直接用 DevEco Studio 打开运行(真机装普通类型 DATA_TRANSFER 必能跑)

八、下一步该学什么?

跑通这个 demo 之后,你的鸿蒙后台任务就入门了。这是韶非 UI 系列第四篇,后续按这个顺序往下:

  1. 数据持久化 @ohos.data.relationalStore(下一篇):鸿蒙 SQLite 封装,结构化数据存取
  2. WebSocket @ohos.net.webSocket:长连接、推送、实时通讯,聊天应用必学
  3. 媒体访问 @ohos.file.photoAccessHelper:访问相册、扫描媒体文件,应用调系统相册必学
  4. 推送通知 @ohos.notificationManager:通知栏展示、点击拉起,离线触达必学
  5. 动画 @ohos.arkui.animation:属性动画、转场动画,UI 进阶必学

写在最后

backgroundTaskManager 的本质,是**「鸿蒙给应用挂后台也能持续跑的原生机制」**——不是浏览器 setTimeout,是鸿蒙专门给后台持续跑的原生机制,能力对标「Wake Lock + Background Sync API」但更安全可控。代价是 BackgroundMode 类型选对多一步、敏感类型加权限多一步。

一旦你开始用后台任务思维写持续跑应用,你会发现大部分「GPS 后台跑」「音乐后台播放」「数据后台同步」的需求,都是 requestSuspendDelay + BackgroundMode 的自然结果。代码量比 setTimeout 多两行,后台持续可控性高九成。

代码已经给你了,仓库链接在上面。现在关掉这篇文章,打开 DevEco Studio,把 demo 跑起来,亲手点发起延迟申请 requestId 感受下后台跑条件申请。

跑通了,回来评论区打个「1」,我看看有多少人真的动手了。🚀


作者:JaneConan
仓库:https://atomgit.com/JaneConan/arkui-background-task
协议:Apache-2.0,随便用,别告我

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐