摘要

BackgroundTask 后台任务模块用于解决应用切后台后定时器、数据同步被系统冻结休眠的问题,分为延时任务、连续后台任务两类,适用于笔记定时同步、倒计时、文件后台上传、日程定时提醒等场景。API Version23 重构后台任务配额管控、自动续期、任务销毁、进程休眠拦截逻辑,修复低版本后台任务提前终止、切后台定时器停止、重复申请任务配额、任务无法取消等兼容缺陷。旧项目升级 API23 后常出现:锁屏后定时同步中断、后台上传中途被系统杀死、多次申请后台任务报错、退出页面任务残留耗电等问题。本文基于 DevEco Studio 适配 OpenHarmony API23 及以上版本,封装全局后台任务工具类,实现短时延时任务、长时同步后台任务、任务取消三大核心能力,搭配笔记定时同步、倒计时两大实战页面提供完整可运行代码,输出配额管控、页面销毁释放、省电适配规范,汇总版本升级兼容故障解决方案,为鸿蒙后台保活业务开发提供标准化实操模板。

关键词

OpenHarmony;ArkUI;API Version23;BackgroundTask;后台任务;后台保活;定时同步;进程休眠拦截

一、引言

1.1 后台任务开发背景

应用切至后台、锁屏后,系统为省电会冻结应用主线程,普通 setInterval 定时器停止运行,文件上传、数据同步、倒计时中断。BackgroundTask 向系统申请后台运行配额,告知系统当前有不可中断业务,系统临时放宽休眠限制,保障任务持续执行。 任务分为两类:

  1. 延时短时任务(DelayTask):单次倒计时、短时同步,最长运行 180s;
  2. 连续后台任务(ContinuousTask):长时间文件上传、云端同步,可手动续期。

OpenHarmony API Version23 针对后台任务调度引擎全面升级,核心变更:

  1. 严格管控应用每日后台任务配额,频繁申请会触发限流;
  2. 新增任务强制销毁机制,页面关闭必须主动取消任务,否则持续占用配额;
  3. 重构前后台切换自动续期逻辑,切前台自动刷新任务时长;
  4. 区分延时任务与连续任务 API,废弃混用接口导致任务提前终止;
  5. 强化省电模式拦截逻辑,低电量时后台任务时长自动缩短;
  6. 修复多任务同时申请导致回调错乱、任务 ID 冲突问题。

大量 API9~11 旧项目升级后锁屏定时任务停止、后台上传中断,根源是未申请 BackgroundTask 配额,仅使用普通定时器,因此掌握 API23 标准后台任务封装规范是同步、定时类功能必备技能。

1.2 开发环境与测试场景

开发工具:DevEco Studio 5.0 及以上 适配系统:OpenHarmony API Version23、HarmonyOS NEXT 导入模块:@ohos.backgroundTaskManager 无需额外动态权限,系统自动管控配额 测试场景:3 分钟倒计时锁屏不中断、笔记后台云端同步、页面退出终止后台任务、多任务并行管控

二、API23+ BackgroundTask 核心 API 与版本变更说明

2.1 模块导入

ets

import backgroundTaskManager from '@ohos.backgroundTaskManager'

2.2 延时短时任务(DelayTask)

  1. requestDelaySuspendTask ():申请短时后台延时任务,返回 taskId
  2. stopDelaySuspendTask (taskId):终止延时任务 适用:倒计时、短时间数据同步,最大 180 秒

2.3 连续长时任务(ContinuousTask)

  1. requestContinuousTask ():申请长时后台运行
  2. renewContinuousTask (taskId):续期延长运行时长
  3. stopContinuousTask (taskId):停止长时任务 适用:文件批量上传、云端大量数据同步

2.4 任务回调事件

  • onExpired:任务时长耗尽,系统强制终止任务触发回调

2.5 API23 废弃与强制约束

  1. 废弃页面销毁不停止后台任务写法,残留任务持续消耗每日配额;
  2. 同一应用同时运行连续任务数量存在上限,不可无限制创建;
  3. 低电量省电模式下后台任务时长减半,不可依赖无限后台运行;
  4. 延时任务最长固定 180s,无法手动续期,超过自动终止;
  5. 后台任务仅用于业务同步,禁止纯挂机保活、广告驻留。

三、API23 标准基础示例代码

3.1 短时延时倒计时任务

ets

import backgroundTaskManager from '@ohos.backgroundTaskManager'

async function startCountDownTask(): Promise<number> {
  const taskInfo: backgroundTaskManager.DelaySuspendTaskInfo = {
    reason: "倒计时任务后台运行",
    isBackground: true
  }
  const taskId = await backgroundTaskManager.requestDelaySuspendTask(taskInfo)
  // 任务过期监听
  backgroundTaskManager.on('expired', (id) => {
    if(id === taskId) console.info("倒计时后台时长耗尽,任务终止")
  })
  return taskId
}

// 停止延时任务
async function stopDelayTask(taskId: number) {
  await backgroundTaskManager.stopDelaySuspendTask(taskId)
}

3.2 长时连续同步任务

ets

async function startSyncTask(): Promise<number> {
  const taskInfo: backgroundTaskManager.ContinuousTaskInfo = {
    reason: "笔记云端后台同步",
    isBackground: true
  }
  const taskId = await backgroundTaskManager.requestContinuousTask(taskInfo)
  // 每60s手动续期
  setInterval(()=>{
    backgroundTaskManager.renewContinuousTask(taskId)
  },60000)
  return taskId
}

async function stopSyncTask(taskId: number) {
  await backgroundTaskManager.stopContinuousTask(taskId)
}

四、两大业务完整实战案例(全兼容 API23)

4.1 实战一:后台任务全局工具类(utils/bgtask_util.ets)

ets

import backgroundTaskManager from '@ohos.backgroundTaskManager'
import promptAction from '@ohos.promptAction'

class BgTaskUtil {
  private static instance: BgTaskUtil
  // 存储运行中任务ID
  private delayTaskIds: number[] = []
  private continuousTaskIds: number[] = []

  static getInstance(): BgTaskUtil {
    if (!BgTaskUtil.instance) {
      BgTaskUtil.instance = new BgTaskUtil()
    }
    return BgTaskUtil.instance
  }

  // 申请短时延时后台任务(最长180s)
  async requestDelayTask(reason: string): Promise<number | null> {
    try {
      const info: backgroundTaskManager.DelaySuspendTaskInfo = {
        reason: reason,
        isBackground: true
      }
      const taskId = await backgroundTaskManager.requestDelaySuspendTask(info)
      this.delayTaskIds.push(taskId)
      // 过期监听
      backgroundTaskManager.on('expired', (id) => {
        const idx = this.delayTaskIds.indexOf(id)
        if (idx > -1) this.delayTaskIds.splice(idx,1)
        promptAction.showToast({message:"后台倒计时时长已耗尽"})
      })
      return taskId
    } catch (err) {
      promptAction.showToast({message:"后台任务申请失败,配额不足"})
      console.error("延时任务申请异常", err)
      return null
    }
  }

  // 停止单个延时任务
  async cancelDelayTask(taskId: number) {
    await backgroundTaskManager.stopDelaySuspendTask(taskId)
    const idx = this.delayTaskIds.indexOf(taskId)
    if(idx > -1) this.delayTaskIds.splice(idx,1)
  }

  // 申请长时连续后台同步任务
  async requestContinuousTask(reason: string): Promise<number | null> {
    try {
      const info: backgroundTaskManager.ContinuousTaskInfo = {
        reason: reason,
        isBackground: true
      }
      const taskId = await backgroundTaskManager.requestContinuousTask(info)
      this.continuousTaskIds.push(taskId)
      // 自动每60s续期
      setInterval(()=>{
        backgroundTaskManager.renewContinuousTask(taskId)
      },60000)
      backgroundTaskManager.on('expired', (id)=>{
        const idx = this.continuousTaskIds.indexOf(id)
        if(idx > -1) this.continuousTaskIds.splice(idx,1)
      })
      return taskId
    } catch (err) {
      promptAction.showToast({message:"长时后台任务申请失败"})
      return null
    }
  }

  // 停止单个长时同步任务
  async cancelContinuousTask(taskId: number) {
    await backgroundTaskManager.stopContinuousTask(taskId)
    const idx = this.continuousTaskIds.indexOf(taskId)
    if(idx > -1) this.continuousTaskIds.splice(idx,1)
  }

  // 页面销毁,清空所有后台任务
  async clearAllTask() {
    // 清空全部延时任务
    for(let id of this.delayTaskIds) {
      await this.cancelDelayTask(id)
    }
    this.delayTaskIds = []
    // 清空全部长时同步任务
    for(let id of this.continuousTaskIds) {
      await this.cancelContinuousTask(id)
    }
    this.continuousTaskIds = []
  }
}

export default BgTaskUtil.getInstance()

4.2 实战二:倒计时延时后台任务页面(锁屏不中断)

ets

import BgTaskUtil from '../utils/bgtask_util'

@Entry
@Component
struct CountDownBgPage {
  @State countNum: number = 180
  timer: number | null = null
  delayTaskId: number | null = null

  async aboutToAppear() {
    this.countNum = 180
  }

  // 开启后台倒计时
  async startCountTask() {
    if(this.timer !== null) return
    // 申请后台延时配额
    this.delayTaskId = await BgTaskUtil.requestDelayTask("倒计时后台运行")
    if(!this.delayTaskId) return
    // 正常倒计时
    this.timer = setInterval(()=>{
      if(this.countNum <= 0) {
        this.stopAll()
        return
      }
      this.countNum -= 1
    },1000)
  }

  // 停止全部定时与后台任务
  async stopAll() {
    if(this.timer) {
      clearInterval(this.timer)
      this.timer = null
    }
    if(this.delayTaskId) {
      await BgTaskUtil.cancelDelayTask(this.delayTaskId)
      this.delayTaskId = null
    }
  }

  build() {
    Column({space:30}) {
      Text(`剩余倒计时:${this.countNum} 秒`)
        .fontSize(26)
        .fontWeight(FontWeight.Bold)

      Button("开启锁屏后台倒计时")
        .width(300)
        .height(48)
        .backgroundColor("#007DFF")
        .onClick(()=>this.startCountTask())

      Button("终止倒计时任务")
        .width(300)
        .height(48)
        .backgroundColor("#f56c6c")
        .onClick(()=>this.stopAll())
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(20)
    .backgroundColor("#F5F5F5")
  }

  async aboutToDisappear() {
    // 页面退出强制停止所有后台任务,释放配额
    await BgTaskUtil.clearAllTask()
    if(this.timer) clearInterval(this.timer)
  }
}

4.3 实战三:笔记云端长时同步页面(连续后台任务)

ets

import BgTaskUtil from '../utils/bgtask_util'
import RdbUtil from '../utils/rdb_util'

@Entry
@Component
struct NoteSyncBgPage {
  syncTaskId: number | null = null
  syncTimer: number | null = null
  @State syncStatus: string = "未开启同步"

  // 开启长时后台笔记同步
  async openSyncTask() {
    if(this.syncTaskId) return
    this.syncTaskId = await BgTaskUtil.requestContinuousTask("笔记云端后台同步")
    if(!this.syncTaskId) return
    this.syncStatus = "同步进行中(锁屏持续运行)"
    // 每10s同步一次数据库笔记
    this.syncTimer = setInterval(async ()=>{
      console.info("执行笔记后台同步逻辑")
      // 此处可调用http上传本地笔记至服务端
    },10000)
  }

  async closeSyncTask() {
    if(this.syncTimer) {
      clearInterval(this.syncTimer)
      this.syncTimer = null
    }
    if(this.syncTaskId) {
      await BgTaskUtil.cancelContinuousTask(this.syncTaskId)
      this.syncTaskId = null
    }
    this.syncStatus = "同步已关闭"
  }

  build() {
    Column({space:25}) {
      Text("笔记云端后台同步")
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
      Text(this.syncStatus).fontSize(18).fontColor("#007DFF")

      Button("开启后台持续同步")
        .width(300)
        .height(48)
        .backgroundColor("#009966")
        .onClick(()=>this.openSyncTask())

      Button("停止后台同步任务")
        .width(300)
        .height(48)
        .backgroundColor("#ff9500")
        .onClick(()=>this.closeSyncTask())
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(20)
    .backgroundColor("#F5F5F5")
  }

  async aboutToDisappear() {
    await BgTaskUtil.clearAllTask()
    if(this.syncTimer) clearInterval(this.syncTimer)
  }
}

五、API23+ 后台任务适配与省电优化规范

5.1 配额管控规范

  1. 后台任务存在每日系统配额,页面退出必须停止任务,避免浪费额度;
  2. 非必要长时同步使用延时 180s 短时任务,减少资源占用;
  3. 低电量、省电模式下自动缩短后台运行时长,不可依赖永久后台。

5.2 任务生命周期规范

  1. 页面aboutToDisappear统一调用工具 clearAllTask 终止全部后台任务;
  2. 延时任务最大 180 秒无续期,长时连续任务定时 renew 手动续期;
  3. 监听 expired 过期回调,任务终止后更新页面状态,提示用户。

5.3 业务分层使用规范

  1. 倒计时、短时数据上传:DelaySuspendTask 延时任务;
  2. 大批量文件、持续云端同步:ContinuousTask 长时连续任务;
  3. 普通页面刷新、UI 定时刷新无需申请后台任务,仅前台运行即可。

5.4 性能耗电规范

  1. 同步轮询间隔不小于 10 秒,高频轮询大幅增加耗电;
  2. 应用切后台闲置时优先停止长时任务,切前台再重新开启;
  3. 禁止无业务目的长期挂后台任务,系统会限制应用配额。

六、API23 升级高频兼容问题与解决方案

问题 1:锁屏后定时器直接停止,倒计时中断 解决:执行定时逻辑前申请 DelaySuspendTask 后台延时任务配额。

问题 2:多次开启后台任务后,再次申请提示失败、配额不足 解决:页面退出主动 stop 任务,回收配额,不残留运行中任务。

问题 3:长时同步任务几分钟后自动停止 解决:使用 ContinuousTask,搭配定时器每 60s 执行 renew 续期。

问题 4:退出页面后台同步仍在后台持续运行耗电 解决:页面 aboutToDisappear 调用 clearAllTask 清空全部任务。

问题 5:升级 API23 后旧版无任务申请,后台完全冻结 解决:所有需要后台运行的定时逻辑,前置申请对应类型后台任务。

问题 6:expired 过期回调不触发 解决:任务 ID 存储数组,回调内匹配对应 ID 再执行状态更新。

七、总结

BackgroundTask 后台任务是 OpenHarmony 解决应用锁屏、后台休眠冻结定时逻辑的标准能力,API Version23 严格管控任务配额、生命周期、自动过期机制,区分短时延时与长时连续两类任务,修复旧版后台任务提前终止、配额泄漏、锁屏中断等核心问题。项目封装全局后台任务工具类,统一管理任务申请、续期、批量销毁,配套倒计时、笔记云端同步两大典型业务页面,适配所有需要后台持续运行定时逻辑的场景。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐