鸿蒙 韶非 UI 系列:后台任务 backgroundTaskManager,延迟挂起 + 持续后台跑,告别前台才活
写在前面
如果你写过鸿蒙 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 = '(未发起)'
// ...
}
三个细节:
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'——backgroundTaskManager是后台任务的入口模块requestId存申请返回的 requestId,用于取消时索引(ArkTS private 成员,不 @State)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 枚举展示:

点「发起延迟」按钮后状态态:状态「已发起延迟挂起,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三姿势)BackgroundMode7 类枚举展示 + 敏感类型权限约束说明- 可直接用 DevEco Studio 打开运行(真机装普通类型 DATA_TRANSFER 必能跑)
八、下一步该学什么?
跑通这个 demo 之后,你的鸿蒙后台任务就入门了。这是韶非 UI 系列第四篇,后续按这个顺序往下:
- 数据持久化
@ohos.data.relationalStore(下一篇):鸿蒙 SQLite 封装,结构化数据存取 - WebSocket
@ohos.net.webSocket:长连接、推送、实时通讯,聊天应用必学 - 媒体访问
@ohos.file.photoAccessHelper:访问相册、扫描媒体文件,应用调系统相册必学 - 推送通知
@ohos.notificationManager:通知栏展示、点击拉起,离线触达必学 - 动画
@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,随便用,别告我
更多推荐


所有评论(0)