欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

前言

在服务端开发中,Linux 的 crontab 是毫无疑问的定时任务王者。只需要一行 0 0 * * * /backup.sh,就能让服务器每天半夜自动备份。

在移动端开发中,我们通常使用 Timer.periodic 来执行循环任务。但 Timer 有几个明显缺陷:

  1. 语法繁琐:很难表达“每周一上午 9 点”这种复杂逻辑。
  2. 不准时:如果上一周期执行时间过长,下一周期会顺延(Drift)。
  3. 重启丢失:App 重启后任务就没了。

cron 是一个纯 Dart 实现的 Crontab 调度库。它完美复刻了 Linux Crontab 的语法,让你在 Dart/Flutter 应用中也能享受 * * * * * 的简洁与强大。

对于 OpenHarmony 开发者,这意味着你可以轻松构建复杂的本地调度逻辑,无论是每日日志清理、定期数据同步,还是闹钟提醒。

一、核心原理与算法解析

1.1 Cron 表达式详解

Cron 表达式由 5 个空格分隔的字段组成:

┌───────────── 分钟 (0 - 59)
│ ┌───────────── 小时 (0 - 23)
│ │ ┌───────────── 日期 (1 - 31)
│ │ │ ┌───────────── 月份 (1 - 12)
│ │ │ │ ┌───────────── 星期 (0 - 6) (0 to 6 are Sunday to Saturday, or use names)
│ │ │ │ │
* * * * *
  • *: 匹配所有值。
  • */5: 每隔 5 个单位。
  • 1,3,5: 枚举值。
  • 10-20: 范围。

1.2 调度算法

cron 库并不是简单地每秒轮询。它是基于 计算下一次执行时间 的机制:

  1. 解析 Cron 表达式,构建时间匹配规则。
  2. 计算 nextRun = parse(expression).next(DateTime.now())
  3. Future.delayed(nextRun - now)
  4. 执行任务,然后重复步骤 2。

这种机制极其高效,不会像 while(true) sleep(1) 那样空耗 CPU。

渲染错误: Mermaid 渲染失败: Parse error on line 3: ... Cron -->|Parse "*/5 * * * *"| Parser -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'STR'

二、核心 API 详解

2.1 基础调度

import 'package:cron/cron.dart';

void main() async {
  final cron = Cron();

  // 1. 每 3 秒执行一次 (仅测试用)
  // 注意:标准 Cron 最小单位是分钟,库扩展支持了秒
  cron.schedule(Schedule.parse('*/3 * * * * *'), () async {
    print('Every 3 seconds: ${DateTime.now()}');
  });

  // 2. 每天上午 10:00 执行
  cron.schedule(Schedule.parse('0 10 * * *'), () async {
    print('Good Morning!');
  });
  
  // 3. 每周五下午 5:30 (下班提醒)
  cron.schedule(Schedule.parse('30 17 * * 5'), () async {
    print('Happy Weekend!');
  });
}

2.2 动态任务管理

schedule 返回一个 ScheduledTask 对象,你可以随时取消它。

final task = cron.schedule(Schedule.parse('*/1 * * * *'), () async {
  print('Running...');
});

// 10秒后停止
await Future.delayed(Duration(seconds: 10));
await task.cancel(); // 停止任务
await cron.close();  // 停止所有任务并释放资源

三、OpenHarmony 平台适配实战

在移动设备上,后台调度是一个极其复杂的话题。因为 Android/OpenHarmony 会杀掉后台进程。

3.1 限制:App 存活期内调度

cron 库是纯 Dart 实现,运行在 Dart Isolate 中。如果 App 被划掉(Kill),Isolate 销毁,cron 自然也就停了。
适用场景

  • App 前台运行时的定时任务(如轮播图、即时消息拉取)。
  • App 后台存活期(Service Foreground)的任务(如音乐播放器切歌、下载进度更新)。

3.2 真正后台:结合 WorkManager

如果你需要 即使 App 被杀掉也能每天 8 点响铃,你不能只用 cron。你需要结合系统的 WorkManagerAlarmManager

在鸿蒙上,我们需要使用 后台代理提醒 (Reminder Agent)后台任务 (Background Task)
Dart 的 cron 负责计算时间,系统的 AlarmManager 负责唤醒。

// 伪代码:结合 WorkManager
import 'package:workmanager/workmanager.dart'; // 需鸿蒙适配

void scheduleDailyBackup() {
  // 1. 使用 cron 计算下次执行时间
  final now = DateTime.now();
  final schedule = Schedule.parse('0 3 * * *'); // 凌晨 3 点
  final nextRun = schedule.next(now);
  
  final delay = nextRun.difference(now);
  
  // 2. 注册系统一次性任务
  Workmanager().registerOneOffTask(
    "daily_backup",
    "backup",
    initialDelay: delay, // 延迟到凌晨 3 点触发
    constraints: Constraints(
      networkType: NetworkType.connected, // 仅 WiFi
      requiresBatteryNotLow: true,
    ),
  );
}

3.3 实战:App 内日志自动清理

假设我们的 App 每天生成大量日志文件。我们需要一个策略:每天 0 点清理 7 天前的文件。

import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:cron/cron.dart';
import 'package:path/path.dart' as p;

class LogCleaner {
  final _cron = Cron();

  void start() {
    // 每天 00:00 执行
    _cron.schedule(Schedule.parse('0 0 * * *'), () async {
      print('Starting log cleanup...');
      await _cleanOldLogs();
    });
  }

  Future<void> _cleanOldLogs() async {
    final dir = await getApplicationDocumentsDirectory();
    final logDir = Directory(p.join(dir.path, 'logs'));
    
    if (!await logDir.exists()) return;

    final now = DateTime.now();
    final sevenDaysAgo = now.subtract(Duration(days: 7));

    await for (var entity in logDir.list()) {
      if (entity is File) {
        final stat = await entity.stat();
        if (stat.modified.isBefore(sevenDaysAgo)) {
          print('Deleting old log: ${entity.path}');
          await entity.delete();
        }
      }
    }
  }

  void stop() => _cron.close();
}

四、高级进阶:时区问题 (TimeZone)

cron 默认使用 DateTime.now(),即系统本地时间。
如果你的 App 涉及跨国业务(如服务器在 UTC,用户在 Beijing),一定要注意。

目前 cron 库对此支持有限。建议在回调函数中手动转换时区,或者将 Cron 表达式设置为 UTC 时间,并在调度前将 DateTime.now().toUtc() 作为基准。

五、总结

cron 是 Dart 生态中处理定时任务的标准答案。它用一种极其通用的语言解决了 “When to run” 的问题。

对于 OpenHarmony 开发者:

  1. 前台任务:直接使用 cron,享受秒级精度。
  2. 后台任务:使用 cron 计算时间差,配合系统级 API (WorkManager / ReminderAgent) 实现唤醒。

最佳实践

  • 不要在 schedule 回调中执行过长的同步操作(会阻塞 Event Loop)。
  • 始终捕获回调中的 try-catch,避免一个任务失败导致整个 Cron 崩溃。

Logo

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

更多推荐