用户睡觉时应用在干什么

我们的截图整理工具有一个“全量深度整理”功能:把过去半年的截图全部重新跑一遍 OCR、人脸检测和智能分类。这个操作极度消耗算力,如果用户一边刷抖音,应用在后台偷偷跑模型,手机早就烫得降频了。

解决这种后台重任务的唯一正解是 WorkScheduler。它的逻辑是“我不规定你几点跑,我只规定条件,系统觉得条件合适了,自然会叫醒你”。最典型的条件就是:设备在充电、连着 Wi-Fi、电量充足。

条件设置的合理阈值

不要在条件里堆叠太多诉求。比如你设置了“必须连着特定 WiFi 且充电且电量>90%”,系统可能要等到海枯石烂才会触发一次。

typescript

import { workScheduler } from '@kit.BackgroundTasksKit';

const workInfo: workScheduler.WorkInfo = {
  workId: 1001,
  bundleName: 'com.example.screenshot.organizer',
  abilityName: 'BatchProcessWorker',
  // 要求网络可用
  networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI,
  // 要求正在充电
  isCharging: true,
  // 要求电量至少 60%
  batteryLevel: 60
};

try {
  workScheduler.startWork(workInfo);
  console.info('后台整理任务注册成功');
} catch (error) {
  console.error(`注册失败: ${error.code}`);
}

startWork 只是注册,不代表立即执行。系统甚至可能三天都不触发一次,这完全取决于用户的使用习惯。

常见错误是以为后台任务可以像定时器一样精准。官方有一个执行窗口的概念,onWorkStart 被拉起后,你大约只有两分钟的连续执行时间。超过两分钟,EnterpriseAdminExtensionAbility 会被系统直接干掉。

断点续传与游标记录

既然只有两分钟,我们就不能指望一次性把半年的截图全部处理完。必须在代码里做好切片和断点。

typescript

import { workScheduler } from '@kit.BackgroundTasksKit';

export default class BatchProcessWorker extends workScheduler.WorkSchedulerExtensionAbility {
  onWorkStart(work: workScheduler.WorkInfo): void {
    // 1. 从 Preferences 或数据库读取上次处理到的图片游标
    let cursor = getLastProcessedCursor();
    
    // 2. 在下一个 Tick 里开启异步处理
    this.processInBatches(cursor);
  }

  async processInBatches(startCursor: number) {
    const batchSize = 20;
    let current = startCursor;
    
    while (Date.now() - this.startTime < 100 * 1000) { // 留 20 秒容错
      const images = await db.getImages(current, batchSize);
      if (images.length === 0) break; // 处理完了
      
      await processImages(images);
      current += images.length;
      saveCursor(current); // 持续落盘游标
    }
  }
}

saveCursor 是这段代码的生命线。每次处理完一批就存一次,下次 onWorkStart 被拉起时接着干。如果没有这个游标,每次系统唤醒都从头开始处理,对于几万张截图的库,你永远也整理不完。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐