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?
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.
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!
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);