HarmonyOS 鸿蒙Next中如何实现定时任务、长时任务和后台数据同步?

HarmonyOS 鸿蒙Next中如何实现定时任务、长时任务和后台数据同步? 在HarmonyOS应用中如何执行后台任务?
如何实现定时任务、长时任务和后台数据同步?

3 回复

解决方案

1. 短时任务(临时后台运行)

import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'
import { BusinessError } from '@ohos.base'

@Entry
@Component
struct ShortBackgroundTask {
  private requestId: number = 0

  build() {
    Column({ space: 16 }) {
      Button('申请短时任务')
        .width('100%')
        .onClick(() => {
          this.requestBackgroundRunning()
        })

      Button('取消短时任务')
        .width('100%')
        .onClick(() => {
          this.stopBackgroundRunning()
        })

      Button('执行后台下载')
        .width('100%')
        .onClick(() => {
          this.performBackgroundTask()
        })
    }
    .padding(16)
  }

  private async requestBackgroundRunning() {
    try {
      // 申请短时任务(应用进入后台后仍可运行)
      await backgroundTaskManager.requestSuspendDelay('download', () => {
        console.log('短时任务即将到期')
        this.stopBackgroundRunning()
      })
      console.log('短时任务申请成功')
    } catch (error) {
      const err = error as BusinessError
      console.error('短时任务申请失败:', err.message)
    }
  }

  private async stopBackgroundRunning() {
    try {
      await backgroundTaskManager.getRemainingDelayTime()
      await backgroundTaskManager.cancelSuspendDelay(this.requestId)
      console.log('短时任务已取消')
    } catch (error) {
      console.error('取消短时任务失败:', error)
    }
  }

  private async performBackgroundTask() {
    await this.requestBackgroundRunning()
    
    // 执行后台任务(如下载文件)
    console.log('开始后台下载...')
    setTimeout(() => {
      console.log('下载完成')
      this.stopBackgroundRunning()
    }, 5000)
  }
}

2. 长时任务(持续后台运行)

import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'
import wantAgent, { WantAgent } from '@ohos.app.ability.wantAgent'
import { BusinessError } from '@ohos.base'
import common from '@ohos.app.ability.common'

@Entry
@Component
struct LongBackgroundTask {
  private context = getContext(this) as common.UIAbilityContext
  private wantAgentObj?: WantAgent

  build() {
    Column({ space: 16 }) {
      Text('长时任务示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Button('开始音乐播放(长时任务)')
        .width('100%')
        .onClick(() => {
          this.startLongRunningTask()
        })

      Button('停止长时任务')
        .width('100%')
        .onClick(() => {
          this.stopLongRunningTask()
        })
    }
    .padding(16)
  }

  private async startLongRunningTask() {
    try {
      // 创建通知WantAgent
      const wantAgentInfo: wantAgent.WantAgentInfo = {
        wants: [
          {
            bundleName: this.context.abilityInfo.bundleName,
            abilityName: this.context.abilityInfo.name
          }
        ],
        requestCode: 0,
        operationType: wantAgent.OperationType.START_ABILITY,
        wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
      }

      this.wantAgentObj = await wantAgent.getWantAgent(wantAgentInfo)

      // 启动长时任务
      await backgroundTaskManager.startBackgroundRunning(
        this.context,
        backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, // 音频播放
        this.wantAgentObj
      )

      console.log('长时任务已启动')
    } catch (error) {
      const err = error as BusinessError
      console.error('启动长时任务失败:', err.message)
    }
  }

  private async stopLongRunningTask() {
    try {
      await backgroundTaskManager.stopBackgroundRunning(this.context)
      console.log('长时任务已停止')
    } catch (error) {
      const err = error as BusinessError
      console.error('停止长时任务失败:', err.message)
    }
  }

  aboutToDisappear() {
    this.stopLongRunningTask()
  }
}

3. 延时任务(WorkScheduler)

import workScheduler from '@ohos.resourceschedule.workScheduler'
import { BusinessError } from '@ohos.base'

// 定义工作信息
interface WorkInfo {
  workId: number
  bundleName: string
  abilityName: string
}

@Entry
@Component
struct DelayedTask {
  private workId: number = 1

  build() {
    Column({ space: 16 }) {
      Text('延时任务管理')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Button('添加网络空闲时执行任务')
        .width('100%')
        .onClick(() => {
          this.addNetworkWork()
        })

      Button('添加充电时执行任务')
        .width('100%')
        .onClick(() => {
          this.addChargingWork()
        })

      Button('添加定时任务')
        .width('100%')
        .onClick(() => {
          this.addTimerWork()
        })

      Button('停止所有延时任务')
        .width('100%')
        .onClick(() => {
          this.stopAllWorks()
        })
    }
    .padding(16)
  }

  private addNetworkWork() {
    try {
      const workInfo: workScheduler.WorkInfo = {
        workId: this.workId++,
        networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI, // WiFi连接时
        bundleName: 'com.example.app',
        abilityName: 'BackgroundWorker'
      }

      workScheduler.startWork(workInfo)
      console.log('网络任务已添加')
    } catch (error) {
      const err = error as BusinessError
      console.error('添加网络任务失败:', err.message)
    }
  }

  private addChargingWork() {
    try {
      const workInfo: workScheduler.WorkInfo = {
        workId: this.workId++,
        isCharging: true, // 充电时执行
        bundleName: 'com.example.app',
        abilityName: 'BackgroundWorker'
      }

      workScheduler.startWork(workInfo)
      console.log('充电任务已添加')
    } catch (error) {
      const err = error as BusinessError
      console.error('添加充电任务失败:', err.message)
    }
  }

  private addTimerWork() {
    try {
      const workInfo: workScheduler.WorkInfo = {
        workId: this.workId++,
        isRepeat: true, // 重复执行
        repeatCycleTime: 3600000, // 每小时执行一次(毫秒)
        bundleName: 'com.example.app',
        abilityName: 'BackgroundWorker'
      }

      workScheduler.startWork(workInfo)
      console.log('定时任务已添加')
    } catch (error) {
      const err = error as BusinessError
      console.error('添加定时任务失败:', err.message)
    }
  }

  private stopAllWorks() {
    try {
      workScheduler.stopAndClearWorks()
      console.log('所有延时任务已停止')
    } catch (error) {
      console.error('停止任务失败:', error)
    }
  }
}

4. 后台数据同步

import workScheduler from '@ohos.resourceschedule.workScheduler'
import preferences from '@ohos.data.preferences'
import http from '@ohos.net.http'

// 后台同步管理类
export class BackgroundSyncManager {
  private static readonly WORK_ID = 100
  private static readonly SYNC_INTERVAL = 1800000 // 30分钟

  /**
   * 启动后台同步任务
   */
  static startSync() {
    try {
      const workInfo: workScheduler.WorkInfo = {
        workId: this.WORK_ID,
        networkType: workScheduler.NetworkType.NETWORK_TYPE_ANY,
        isRepeat: true,
        repeatCycleTime: this.SYNC_INTERVAL,
        isPersisted: true, // 持久化
        bundleName: 'com.example.app',
        abilityName: 'SyncWorker'
      }

      workScheduler.startWork(workInfo)
      console.log('后台同步已启动')
    } catch (error) {
      console.error('启动后台同步失败:', error)
    }
  }

  /**
   * 停止后台同步
   */
  static stopSync() {
    try {
      workScheduler.stopWork(this.WORK_ID, false)
      console.log('后台同步已停止')
    } catch (error) {
      console.error('停止后台同步失败:', error)
    }
  }

  /**
   * 执行同步任务
   */
  static async performSync() {
    console.log('开始数据同步...')

    try {
      // 1. 检查网络状态
      // 2. 获取本地数据
      const localData = await this.getLocalData()

      // 3. 上传到服务器
      await this.uploadData(localData)

      // 4. 下载最新数据
      const remoteData = await this.downloadData()

      // 5. 保存到本地
      await this.saveLocalData(remoteData)

      // 6. 更新同步时间
      await this.updateSyncTime()

      console.log('数据同步完成')
    } catch (error) {
      console.error('数据同步失败:', error)
    }
  }

  private static async getLocalData(): Promise<string> {
    // 从Preferences读取本地数据
    const pref = await preferences.getPreferences(
      getContext(),
      'sync_data'
    )
    return await pref.get('data', '') as string
  }

  private static async uploadData(data: string): Promise<void> {
    const httpRequest = http.createHttp()
    try {
      await httpRequest.request('https://api.example.com/upload', {
        method: http.RequestMethod.POST,
        extraData: data
      })
    } finally {
      httpRequest.destroy()
    }
  }

  private static async downloadData(): Promise<string> {
    const httpRequest = http.createHttp()
    try {
      const response = await httpRequest.request(
        'https://api.example.com/data'
      )
      return response.result as string
    } finally {
      httpRequest.destroy()
    }
  }

  private static async saveLocalData(data: string): Promise<void> {
    const pref = await preferences.getPreferences(
      getContext(),
      'sync_data'
    )
    await pref.put('data', data)
    await pref.flush()
  }

  private static async updateSyncTime(): Promise<void> {
    const pref = await preferences.getPreferences(
      getContext(),
      'sync_data'
    )
    await pref.put('last_sync_time', Date.now())
    await pref.flush()
  }
}

// 使用示例
@Entry
@Component
struct BackgroundSyncDemo {
  @State lastSyncTime: string = '从未同步'

  aboutToAppear() {
    this.loadSyncTime()
  }

  build() {
    Column({ space: 16 }) {
      Text('后台数据同步')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Text('上次同步: ' + this.lastSyncTime)
        .fontSize(14)
        .fontColor('#666666')

      Button('启动自动同步')
        .width('100%')
        .onClick(() => {
          BackgroundSyncManager.startSync()
        })

      Button('立即同步')
        .width('100%')
        .onClick(async () => {
          await BackgroundSyncManager.performSync()
          await this.loadSyncTime()
        })

      Button('停止自动同步')
        .width('100%')
        .onClick(() => {
          BackgroundSyncManager.stopSync()
        })
    }
    .padding(16)
  }

  private async loadSyncTime() {
    try {
      const pref = await preferences.getPreferences(
        getContext(),
        'sync_data'
      )
      const timestamp = await pref.get('last_sync_time', 0) as number
      if (timestamp > 0) {
        const date = new Date(timestamp)
        this.lastSyncTime = date.toLocaleString('zh-CN')
      }
    } catch (error) {
      console.error('加载同步时间失败:', error)
    }
  }
}

5. 定时提醒任务

import reminderAgentManager from '@ohos.reminderAgentManager'
import { BusinessError } from '@ohos.base'

@Entry
@Component
struct ReminderTask {
  build() {
    Column({ space: 16 }) {
      Text('定时提醒')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Button('设置闹钟提醒')
        .width('100%')
        .onClick(() => {
          this.setAlarmReminder()
        })

      Button('设置倒计时提醒')
        .width('100%')
        .onClick(() => {
          this.setCountDownReminder()
        })

      Button('取消所有提醒')
        .width('100%')
        .onClick(() => {
          this.cancelAllReminders()
        })
    }
    .padding(16)
  }

  private async setAlarmReminder() {
    try {
      const reminderRequest: reminderAgentManager.ReminderRequestAlarm = {
        reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_ALARM,
        hour: 8,
        minute: 30,
        daysOfWeek: [1, 2, 3, 4, 5], // 工作日
        title: '起床闹钟',
        ringDuration: 60,
        snoozeTimes: 3,
        timeInterval: 300
      }

      const reminderId = await reminderAgentManager.publishReminder(reminderRequest)
      console.log('闹钟提醒已设置, ID:', reminderId)
    } catch (error) {
      const err = error as BusinessError
      console.error('设置闹钟失败:', err.message)
    }
  }

  private async setCountDownReminder() {
    try {
      const reminderRequest: reminderAgentManager.ReminderRequestTimer = {
        reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_TIMER,
        triggerTimeInSeconds: 300, // 5分钟后
        title: '倒计时提醒',
        content: '时间到!'
      }

      const reminderId = await reminderAgentManager.publishReminder(reminderRequest)
      console.log('倒计时提醒已设置, ID:', reminderId)
    } catch (error) {
      const err = error as BusinessError
      console.error('设置倒计时失败:', err.message)
    }
  }

  private async cancelAllReminders() {
    try {
      await reminderAgentManager.cancelAllReminders()
      console.log('所有提醒已取消')
    } catch (error) {
      console.error('取消提醒失败:', error)
    }
  }
}

关键要点

  1. 短时任务: 应用切换后台后延长运行时间,最多3分钟
  2. 长时任务: 需要用户感知,显示前台通知,支持音频、导航等场景
  3. 延时任务: 系统根据条件(网络、充电等)择机执行
  4. 定时提醒: 到达指定时间触发通知
  5. 权限要求: 后台任务需要在module.json5中声明权限

最佳实践

  1. 合理选择: 根据场景选择合适的后台任务类型
  2. 及时取消: 任务完成后立即取消,避免资源浪费
  3. 电量优化: 延时任务设置合理的执行条件
  4. 错误处理: 捕获并处理后台任务异常
  5. 用户体验: 长时任务提供清晰的通知信息

更多关于HarmonyOS 鸿蒙Next中如何实现定时任务、长时任务和后台数据同步?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,定时任务使用@ScheduledTask装饰器实现,通过taskDispatcher调度。长时任务需在module.json5中声明ContinuousTask权限,使用continuousTaskManager管理。后台数据同步通过BackgroundTaskManager注册DataSync类型任务实现,系统会智能调度资源。

在HarmonyOS Next中,后台任务管理主要通过后台任务管理器(Background Task Manager, BTM)长时任务(Long-term Task) 机制实现,以确保应用在后台或设备休眠时仍能执行关键操作,同时兼顾系统资源与功耗。

1. 定时任务

定时任务适用于按固定时间间隔执行的后台操作,如数据同步、通知推送等。主要通过 workScheduler 模块实现。

关键步骤:

  • 配置权限:在 module.json5 中声明 ohos.permission.KEEP_BACKGROUND_RUNNING 权限。
  • 创建 WorkInfo 对象:定义任务触发条件(如网络状态、充电状态、重复间隔等)。
  • 调度任务:使用 workScheduler.startWork() 提交任务。
  • 处理任务:在 EntryAbility 中实现 onBackgroundWork() 回调以执行具体逻辑。

示例代码:

import workScheduler from '@ohos.workScheduler';

// 创建WorkInfo对象
let workInfo: workScheduler.WorkInfo = {
  workId: 1,
  bundleName: "com.example.app",
  abilityName: "EntryAbility",
  networkType: workScheduler.NetworkType.NETWORK_TYPE_ANY, // 触发条件:任意网络
  repeatCycleTime: 10 * 60 * 1000, // 重复间隔10分钟
  isRepeat: true
};

// 调度任务
workScheduler.startWork(workInfo).then(() => {
  console.log("定时任务调度成功");
});

2. 长时任务

长时任务适用于需持续运行的后台操作(如音乐播放、导航、文件下载等)。通过 长时任务管理器 申请长时任务资格,避免被系统挂起。

关键步骤:

  • 声明权限:在 module.json5 中声明 ohos.permission.KEEP_BACKGROUND_RUNNING
  • 申请长时任务:在 UIAbilityonCreate()onForeground() 中调用 长时任务管理器 的 API 申请。
  • 释放资源:在任务完成或 onBackground() 时释放长时任务资格。

示例代码:

import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import { BusinessError } from '@ohos.base';

// 申请长时任务
let id: number;
try {
  let context = getContext(this) as common.UIAbilityContext;
  id = backgroundTaskManager.requestSuspendDelay("长时任务描述", () => {
    // 回调:系统即将挂起应用前的通知,可在此保存状态
    console.log("应用即将被挂起");
  });
  console.log("长时任务申请成功,ID: " + id);
} catch (error) {
  console.error("申请长时任务失败: " + (error as BusinessError).message);
}

// 任务完成后释放
backgroundTaskManager.cancelSuspendDelay(id);

3. 后台数据同步

后台数据同步通常结合定时任务或长时任务实现,确保数据在后台定期更新或持续同步。

推荐方案:

  • 使用 workScheduler:配置网络触发条件,在 onBackgroundWork() 中执行同步逻辑。
  • 结合长时任务:若同步耗时较长,可申请长时任务资格保障执行。
  • 使用 @ohos.data.distributedData:若涉及跨设备数据同步,可使用分布式数据管理。

示例(定时同步):

// 在EntryAbility中实现onBackgroundWork回调
onBackgroundWork(workInfo: workScheduler.WorkInfo) {
  console.log("后台数据同步开始");
  // 执行数据同步逻辑,如调用API更新本地数据
  this.syncData();
}

private syncData() {
  // 具体同步实现
}

注意事项

  • 功耗与资源:后台任务应轻量化,避免频繁唤醒设备或占用过多资源。
  • 生命周期管理:及时释放长时任务资格,避免影响系统性能。
  • 触发条件:合理设置 WorkInfo 的触发条件(如充电状态、网络类型),以降低功耗。

通过上述机制,HarmonyOS Next 可有效支持定时任务、长时任务及后台数据同步,开发者需根据场景选择合适方案并遵循系统规范。

回到顶部