将Android中定期工作管理器的初始延迟设置为一个时间段。

21

我有一个Worker实例,需要每24小时运行一次,这对于PeriodicWorkRequest API来说相当简单。但是有一个问题。

如果用户在晚上8点开始工作,我需要第一次工作管理器在第二天早上9点运行,然后遵循24小时的周期限制。

我在这里找到了OneTimeWorkRequest API有一个可以使用的setInitialDelay()函数,但我没有找到任何关于PeriodicWork API的内容。

有一些hack方法可以解决,例如我可以使用带有初始延迟的OneTimeWork,然后从那里安排PeriodicWork,但这是一种比较麻烦的方式。

是否有办法只使用PeriodicWorkRequest API来完成?

3个回答

17

在2019年5月16日发布的最新版本的Work manager(版本2.1.0-alpha02),定期工作请求现在支持初始延迟。您可以使用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);

3
setInitialDelay 的工作方式很奇怪。当我设置了启动时间为15分钟和初始延迟为1分钟时,工作程序会在16分钟后开始运行。我原以为它会立即启动(就像 OneTimeWorkRequest 一样),但实际上是在1分钟后启动的(而不是16分钟后)。之后的启动将每隔15分钟进行一次。 - proninyaroslav
4
定期工作的最小间隔为15分钟,且不能有初始延迟。MIN_PERIODIC_INTERVAL_MILLIS为15分钟。详见https://developer.android.com/reference/kotlin/androidx/work/PeriodicWorkRequest#min_periodic_interval_millis - Anjal Saneen
2
PeriodicWorker上无法设置初始延迟,初始延迟仅适用于oneTimeWorker。 - SmartAndroidian
2
我正在使用版本implementation 'android.arch.work:work-runtime:1.0.1',但是这里没有setInitialDelay()。很抱歉,我正在使用稳定版的WorkManager而不是alpha版。 - SmartAndroidian
@SmartAndroidian 你可以使用稳定版本 2.2.0-rc01。它也在那里。 - Anjal Saneen
显示剩余3条评论

12
PeriodicWorkRequest有一个很好的Builder构造函数,您可以传递一个间隔时间让工作执行。您可以在这里查看Builder和每个参数的详细信息。

因此,要将初始延迟设置为定期工作,您可以按照以下方式进行:

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 被忽略了... 目前有合理的解决方案吗? - Michał Powłoka

4

随着WorkManagerv1.0.0-alpha07)当前的alpha版本,我认为目前还不可能为PeriodicWorkRequest设置初始延迟。也许在以后的版本中我们会得到一些API。

暂时来说,就像你说的那样,您可以使用带有初始延迟的OneTimeWork请求设置,然后将一个PeriodicWork请求加入到WorkManager的队列中。

我想说这是一个技巧,但并不太糟糕。


是的,那是我的当前策略。我只是想知道是否有任何简单直接的方法。 - Sriram R
@qasim,你有一些代码,如何实现这个? - Noor Hossain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接