Set initial delay to a Periodic Work Manager in Android
Asked Answered
M

3

22

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?

Milden answered 21/8, 2018 at 9:18 Comment(0)
C
17

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);
Centonze answered 22/5, 2019 at 6:12 Comment(8)
setInitialDelay works weird. When I set a startup period of 15 minutes and the initial delay of 1 minute, the worker starts after 16 minutes. I thought it would immediately (as OneTimeWorkRequest), but after 1 minute (and not after 16 minutes). And the subsequent starts will be every 15 minutes.Katharyn
@Katharyn Periodic work has a minimum interval of 15 minutes and it cannot have an initial delay. MIN_PERIODIC_INTERVAL_MILLIS is 15 minute. developer.android.com/reference/kotlin/androidx/work/…Centonze
Thank you. I will try to do a trick with OneTimeWorkRequestKatharyn
Initial delay cannot be set on PeriodicWorker, initial delay is for oneTimeWorker onlyGabby
@Gabby Can you check the version of Work manager? it should be Version 2.1.0-alpha02+ to get the setInitialDelay().Centonze
I am using the version implementation 'android.arch.work:work-runtime:1.0.1' and here setInitialDelay() is not there. Sorry I am using stable version of WorkManager and not alpha.Gabby
@Gabby You can use stable version 2.2.0-rc01. it's there as well.Centonze
A little clarification, this method (setInitialDelay) does not work for a worker on android 23, worker version 2.4.0Maund
B
12

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!

Bede answered 28/1, 2019 at 2:52 Comment(1)
Docs say that flexTime is ignored on certain API versions (23 for example)... Is there any reasonable solution yet?Veracity
A
4

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.

Actinolite answered 21/8, 2018 at 10:56 Comment(2)
Yeah, that's my current strategy. I was just wondering if there was any straightforward way.Milden
@qasim, have some codes, how to achieve this ?Karlykarlyn

© 2022 - 2024 — McMap. All rights reserved.