Set initial delay to a Periodic Work Manager in An

2020-02-23 09:18发布

问题:

I have a Worker instance that needs to run every 24 hours which is pretty simple considering the PeriodicWorkRequest API. But here's the catch.

If the user initiates the work at say 8 PM, I need the first instance of the work manager to run at 9 AM the next morning and then follow the 24-hour periodic constraint.

I looked hereand I found that the OneTimeWorkRequest API has a setInitialDelay() function that can be used but I wasn't able to find anything for the PeriodicWork API.

Ther are some hacks for this such as I can use the OneTimeWork with the initial delay and then schedule a PeriodicWork from there but it's kinda a dirty hack.

Is there any way to do this with just the PeriodicWorkRequest API?

回答1:

With the current alpha release of WorkManager (v1.0.0-alpha07), i think it's not possible to set initial delay for the PeriodicWorkReqeust. May be we'll get some API in next releases.

For the time being, as you said, you can use a OneTimeWork request setup with initial delay, which will then en-queue a PeriodicWork request to WorkManager.

I would say it a hack but not that much dirty.



回答2:

PeriodicWorkRequest has a nice Builder constructor where you can pass an interval for which the work can execute. You can check the details of the Builder and each parameter here

So, to set an initial delay to your periodic work you can do like this:

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);

Here is where the magic happens:

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);
}

Please be aware that this time is not exact. WorkManager may trigger your job at any time in that flex window depending on your constraints, system load, other scheduled jobs, etc.

If you want a precise timing, switch to AlarmManager.

Hope it helps!



回答3:

On the new version of Work manager (Version 2.1.0-alpha02 released on May 16, 2019) PeriodicWorkRequests now support initial delays. You can use the setInitialDelay method on PeriodicWorkRequest.Builder to set an initial delay.

Example:

    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);