在 Android 中为定期工作管理器设置初始延迟

Posted

技术标签:

【中文标题】在 Android 中为定期工作管理器设置初始延迟【英文标题】:Set initial delay to a Periodic Work Manager in Android 【发布时间】:2019-01-27 10:49:07 【问题描述】:

我有一个 Worker 实例,它需要每 24 小时运行一次,考虑到 PeriodicWorkRequest API,这非常简单。但这就是问题所在。

如果用户在晚上 8 点开始工作,我需要工作管理器的第一个实例在第二天早上 9 点运行,然后遵循 24 小时定期约束。

我查看了here,发现 OneTimeWorkRequest API 有一个可以使用的setInitialDelay() 函数,但我找不到PeriodicWork API 的任何内容。

对此有一些技巧,例如我可以使用 OneTimeWork 与初始延迟,然后从那里安排 PeriodicWork,但这有点肮脏的技巧。

有没有办法只使用PeriodicWorkRequest API 来做到这一点?

【问题讨论】:

【参考方案1】:

在新版本的工作管理器上(Version 2.1.0-alpha02 于 2019 年 5 月 16 日发布)PeriodicWorkRequests 现在支持初始延迟。您可以使用 PeriodicWorkRequest.Builder 上的 setInitialDelay 方法来设置初始延迟。

例子:

    PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(
        WorkerReminderPeriodic.class,
        24,
        TimeUnit.HOURS,
        PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS,
        TimeUnit.MILLISECONDS)
      .setInitialDelay(1, TimeUnit.HOURS)
      .addTag("send_reminder_periodic")
      .build();


    WorkManager.getInstance()
        .enqueueUniquePeriodicWork("send_reminder_periodic", ExistingPeriodicWorkPolicy.REPLACE, workRequest);

【讨论】:

setInitialDelay 工作很奇怪。当我设置 15 分钟的启动时间和 1 分钟的初始延迟时,工作人员会在 16 分钟后启动。我认为它会立即(如OneTimeWorkRequest),但在 1 分钟后(而不是在 16 分钟后)。随后的启动将是每 15 分钟一次。 @proninyaroslav 定期工作的最小间隔为 15 分钟,并且不能有初始延迟。 MIN_PERIODIC_INTERVAL_MILLIS 为 15 分钟。 developer.android.com/reference/kotlin/androidx/work/… 谢谢。我会尝试用OneTimeWorkRequest做一个技巧 PeriodicWorker 无法设置初始延迟,初始延迟仅适用于 oneTimeWorker 我使用的是 implementation 'android.arch.work:work-runtime:1.0.1' 版本,而这里 setInitialDelay() 不存在。抱歉,我使用的是稳定版 WorkManager 而不是 alpha。【参考方案2】:

PeriodicWorkRequest 有一个不错的 Builder 构造函数,您可以在其中传递工作可以执行的时间间隔。可以查看Builder的详细信息和各个参数here

因此,要为您的定期工作设置初始延迟,您可以这样做:

int hourOfTheDay = 10; // When to run the job
int repeatInterval = 1; // In days

long flexTime = calculateFlex(hourOfTheDay, repeatInterval);

Constraints myConstraints = new Constraints.Builder()
        .setRequiresBatteryNotLow(true)
        .build();

PeriodicWorkRequest workRequest =
        new PeriodicWorkRequest.Builder(MyNiceWorker.class,
                repeatInterval, TimeUnit.DAYS,
                flexTime, TimeUnit.MILLISECONDS)
                .setConstraints(myConstraints)
                .build();

WorkManager.getInstance().enqueueUniquePeriodicWork(YOUR_NICE_WORK_TAG,
        ExistingPeriodicWorkPolicy.REPLACE,
        workRequest);

这就是神奇发生的地方:

private long calculateFlex(int hourOfTheDay, int periodInDays) 

    // Initialize the calendar with today and the preferred time to run the job.
    Calendar cal1 = Calendar.getInstance();
    cal1.set(Calendar.HOUR_OF_DAY, hourOfTheDay);
    cal1.set(Calendar.MINUTE, 0);
    cal1.set(Calendar.SECOND, 0);

    // Initialize a calendar with now.
    Calendar cal2 = Calendar.getInstance();

    if (cal2.getTimeInMillis() < cal1.getTimeInMillis()) 
        // Add the worker periodicity.
        cal2.setTimeInMillis(cal2.getTimeInMillis() + TimeUnit.DAYS.toMillis(periodInDays));
    

    long delta = (cal2.getTimeInMillis() - cal1.getTimeInMillis());

    return ((delta > PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS) ? delta
            : PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS);

请注意,这个时间并不准确。 WorkManager 可能会在该弹性窗口中随时触发您的作业,具体取决于您的约束、系统负载、其他计划作业等。

如果您想要精确计时,请切换到 AlarmManager

希望对你有帮助!

【讨论】:

文档说某些 API 版本(例如 23 版本)忽略了 flexTime...有没有合理的解决方案?【参考方案3】:

WorkManager (v1.0.0-alpha07) 的当前 alpha 版本中,我认为无法为PeriodicWorkReqeust 设置初始延迟。也许我们会在下一个版本中获得一些 API。

目前,正如您所说,您可以使用带有初始延迟的 OneTimeWork 请求设置,然后将 PeriodicWork 请求排队到 WorkManager。

我会说这是一个 hack,但没那么脏。

【讨论】:

是的,这是我目前的策略。我只是想知道是否有任何直接的方法。 @qasim,有一些代码,如何实现?

以上是关于在 Android 中为定期工作管理器设置初始延迟的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Prism 中为我的对话窗口设置区域管理器?

小米和OPPO等中国ROM的工作管理器,在电池优化时,将预定的工作延迟增加几个小时

查找嵌入式 Linux 系统中的延迟问题(停顿)

如何在 SwiftUI 中延迟 @State 变量的设置值

如何获取前两个选择器意图并仅在android中为用户显示它们

数字选择器中的跨区文本