Schedule a work on a specific time with WorkManager

AndroidAndroid Workmanager

Android Problem Overview


> WorkManager is a library used to enqueue work that is guaranteed to execute after its constraints are met.

Hence, After going though the Constraints class I haven't found any function to add time constraint on the work. For like example, I want to start a work to perform at 8:00am (The work can be any of two types OneTimeWorkRequest or PeriodicWorkRequest) in the morning. How can I add constraint to schedule this work with WorkManager.

Android Solutions


Solution 1 - Android

Unfortunately, you cannot schedule a work at specific time as of now. If you have time critical implementation then you should use AlarmManager to set alarm that can fire while in Doze to by using setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

You can schedule a work, with onetime initial delay or execute it periodically, using the WorkManager as follows:

Create Worker class:

public class MyWorker extends Worker {
    @Override
    public Worker.WorkerResult doWork() {

        // Do the work here
  
        // Indicate success or failure with your return value:
        return WorkerResult.SUCCESS;

        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}

Then schedule OneTimeWorkRequest as follows:

OneTimeWorkRequest mywork=
        new OneTimeWorkRequest.Builder(MyWorker.class)
        .setInitialDelay(<duration>, <TimeUnit>)// Use this when you want to add initial delay or schedule initial work to `OneTimeWorkRequest` e.g. setInitialDelay(2, TimeUnit.HOURS)
        .build();
WorkManager.getInstance().enqueue(mywork);

You can setup additional constraints as follows:

// Create a Constraints that defines when the task should run
Constraints myConstraints = new Constraints.Builder()
    .setRequiresDeviceIdle(true)
    .setRequiresCharging(true)
    // Many other constraints are available, see the
    // Constraints.Builder reference
     .build();

Then create a OneTimeWorkRequest that uses those constraints

OneTimeWorkRequest mywork=
                new OneTimeWorkRequest.Builder(MyWorker.class)
     .setConstraints(myConstraints)
     .build();
WorkManager.getInstance().enqueue(mywork);

PeriodicWorkRequest can be created as follows:

 PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(MyWorker.class, 12, TimeUnit.HOURS)
                                   .build();
  WorkManager.getInstance().enqueue(periodicWork);

This creates a PeriodicWorkRequest to run periodically once every 12 hours.

Solution 2 - Android

PeriodicWorkRequests now support initial delays from the version 2.1.0-alpha02. You can use the setInitialDelay method on PeriodicWorkRequest.Builder to set an initial delay. Link Here

Example of schedule at every day at 8:00 am. here I'm using joda time library for time operations.

final int SELF_REMINDER_HOUR = 8;
    
    if (DateTime.now().getHourOfDay() < SELF_REMINDER_HOUR) {
        delay = new Duration(DateTime.now() , DateTime.now().withTimeAtStartOfDay().plusHours(SELF_REMINDER_HOUR)).getStandardMinutes();
    } else {
        delay = new Duration(DateTime.now() , DateTime.now().withTimeAtStartOfDay().plusDays(1).plusHours(SELF_REMINDER_HOUR)).getStandardMinutes();
    }


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


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

Solution 3 - Android

So far it's not possible to achieve exact times using PeriodicWorkRequest.
An ugly work-around would be using a OneTimeWorkRequest and when it fires, set another OneTimeWorkRequest with a new calculated period, and so on.

Solution 4 - Android

All answers are now obsolete, upgrade to WorkManager 2.1.0-alpha02 (or beyond) setInitialDelay() method used to work only for OneTimeWorkRequest, but now they also support PeriodicWorkRequest.

implementation "androidx.work:work-runtime:2.1.0-alpha02"

> PeriodicWorkRequests now support initial delays. You can use the > setInitialDelay method on PeriodicWorkRequest.Builder to set an > initial delay

quick example:

new PeriodicWorkRequest.Builder(MyWorker.class, MY_REPEATS, TimeUnit.HOURS)
        .setInitialDelay(THE_DELAY,TimeUnit.SECONDS);

Solution 5 - Android

I might be somewhat late but anyway I did this in order to schedule a WorkRequest at a given time (with an optional short delay). You just need to get the time from a TimePicker of whatever:

public static void scheduleWork(int hour, int minute) {
    Calendar calendar = Calendar.getInstance();
    long nowMillis = calendar.getTimeInMillis();

    if(calendar.get(Calendar.HOUR_OF_DAY) > hour ||
            (calendar.get(Calendar.HOUR_OF_DAY) == hour && calendar.get(Calendar.MINUTE)+1 >= minute)) {
        calendar.add(Calendar.DAY_OF_MONTH, 1);
    }

    calendar.set(Calendar.HOUR_OF_DAY,hour);
    calendar.set(Calendar.MINUTE,minute);

    calendar.set(Calendar.SECOND,0);
    calendar.set(Calendar.MILLISECOND,0);
    long diff = calendar.getTimeInMillis() - nowMillis;

    WorkManager mWorkManager = WorkManager.getInstance();
    Constraints constraints = new Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build();
    mWorkManager.cancelAllWorkByTag(WORK_TAG);
    OneTimeWorkRequest mRequest = new OneTimeWorkRequest.Builder(NotificationWorker.class)
            .setConstraints(constraints)
            .setInitialDelay(diff,TimeUnit.MILLISECONDS)
            .addTag(WORK_TAG)
            .build();
    mWorkManager.enqueue(mRequest);

}

Solution 6 - Android

You can use AndroidJob from evernote

class NotificationJob : DailyJob() {

override fun onRunDailyJob(params: Params): DailyJobResult {
       //your job       
       return DailyJobResult.SUCCESS

}

companion object {
    val TAG = "NotificationJob"
    fun scheduleJob() {
        //scheduled between 9-10 am

        DailyJob.schedule(JobRequest.Builder(TAG), 
            TimeUnit.HOURS.toMillis(9),TimeUnit.HOURS.toMillis(10 ))
     }
   }
 }

and a notification creator

  class NotificationJobCreator : JobCreator {

        override fun create(tag: String): Job? {
           return when (tag) {
              NotificationJob.TAG ->
                NotificationJob()
              else ->
                null
           }
       }
  }

then initiate in your Application class

    JobManager.create(this).addJobCreator(NotificationJobCreator())

The gradle dependency is

    dependencies {
        implementation 'com.evernote:android-job:1.2.6'

        // or this with workmnager 
        implementation 'com.evernote:android-job:1.3.0-alpha08'
    }

Solution 7 - Android

I tried OneTimeWorkRequest but it is flaky(work only sometimes) so we should not rely on it. AlarmManager is a better option.

Solution 8 - Android

If you want to set an initial delay to a PeriodicWorkRequest, I presented the solution here:

https://stackoverflow.com/questions/51945439/set-initial-delay-to-a-periodic-work-manager-in-android/54394957#54394957

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionS HaqueView Question on Stackoverflow
Solution 1 - AndroidSagarView Answer on Stackoverflow
Solution 2 - AndroidAnjal SaneenView Answer on Stackoverflow
Solution 3 - Androidmhashim6View Answer on Stackoverflow
Solution 4 - AndroidusernotnullView Answer on Stackoverflow
Solution 5 - AndroidIgnacio GarciaView Answer on Stackoverflow
Solution 6 - AndroidUzairView Answer on Stackoverflow
Solution 7 - AndroidNurseyit TursunkulovView Answer on Stackoverflow
Solution 8 - AndroidRafael ChagasView Answer on Stackoverflow