82 votes

Planifier un travail à une heure précise avec WorkManager

WorkManager est une bibliothèque utilisée pour mettre en file d'attente des travaux dont l'exécution est garantie une fois les contraintes respectées.

Donc, après avoir passé en revue les Contraintes Je n'ai pas trouvé de fonction permettant d'ajouter une contrainte de temps au travail. Par exemple, je veux commencer un travail à effectuer à 8h00 (Le travail peut être de deux types différents Demande de travail à temps partiel o Demande de travail périodique ) le matin. Comment puis-je ajouter une contrainte pour planifier ce travail avec WorkManager.

4 votes

0 votes

Je pense que cela peut être un bon moyen de contourner le problème. Pour cela, je dois calculer la différence de temps entre l'heure actuelle et l'heure à laquelle je veux que le travail commence. Mais puis-je définir l'heure de début de façon explicite ?

0 votes

Quel est le problème du calcul ? timeInFutureInSeconds - timeNowInSeconds ?

65voto

Sagar Points 12352

Malheureusement, il n'est pas possible pour l'instant de programmer un travail à une heure précise. Si vous avez une mise en œuvre critique en termes de temps, vous devriez utiliser AlarmManager pour définir une alarme qui peut se déclencher pendant la mise en veille en utilisant la fonction setAndAllowWhileIdle() o setExactAndAllowWhileIdle() .

Vous pouvez planifier un travail, avec un délai initial unique, ou l'exécuter périodiquement, en utilisant l'outil de planification de l'exécution. WorkManager comme suit :

Créer la classe Worker :

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

Ensuite, programmez OneTimeWorkRequest comme suit :

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

Vous pouvez définir des contraintes supplémentaires comme suit :

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

Créez ensuite une demande de travail à temps partiel qui utilise ces contraintes.

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

La demande de travail périodique peut être créée comme suit :

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

Cela crée une demande de travail périodique (PeriodicWorkRequest) à exécuter périodiquement une fois toutes les 12 heures.

0 votes

Merci pour la réponse, mais cela ne va-t-il pas démarrer le travail immédiatement ? Je voulais le programmer pour qu'il commence plus tard.

0 votes

Utilice setInitialDelay(long duration, TimeUnit timeUnit) sur OneTimeWorkRequest.Builder

6 votes

Le problème est que cela ne fonctionne pas pour les travaux périodiques.

21voto

Anjal Saneen Points 1400

Les PeriodicWorkRequests prennent désormais en charge les délais initiaux à partir de la version 2.1.0-alpha02. Vous pouvez utiliser la méthode setInitialDelay de PeriodicWorkRequest.Builder pour définir un délai initial. Lien ici

Exemple d'horaire à tous les jours à 8h00. Ici j'utilise bibliothèque temporelle joda pour les opérations de temps.

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

0 votes

Je crois qu'à l'avenir, le décalage du temps d'exécution sera très important... Mais avec REPLACE il semble que tout va bien

0 votes

Pour moi, il va licencier le travailleur périodique à la 1ère fois seulement à la fin de la période de 24 heures. Y a-t-il un moyen de le faire fonctionner juste après la création ? (par exemple, il devrait être lancé à 17:50, mis en file d'attente à 17:40 et lancé après 10 minutes pour la première fois).

0 votes

De plus, si l'utilisateur modifie ses paramètres horaires, les travailleurs seront intacts (par exemple, le travailleur devrait fonctionner tous les jours à 8h00. Si l'utilisateur définit un autre fuseau horaire, +3h de plus par exemple, il ne sera pas exécuté à 11h00).

16voto

UpsideDownTree Points 124

Jusqu'à présent, il n'est pas possible d'obtenir des temps exacts en utilisant PeriodicWorkRequest .
Une solution de contournement peu élégante consisterait à utiliser un fichier OneTimeWorkRequest et quand il s'enflamme, mettez-en un autre OneTimeWorkRequest avec une nouvelle période calculée, et ainsi de suite.

30 votes

Wow... C'est assez dégoûtant...

1 votes

Je ne crois pas qu'il y ait une raison de passer d'Evernote à Android-job alors

0 votes

Jetez un coup d'œil à ma réponse ici : stackoverflow.com/questions/51945439/

7voto

Ignacio Garcia Points 21

Je suis peut-être un peu en retard, mais j'ai quand même fait cela pour programmer une demande de travail à une heure donnée (avec un court délai optionnel). Il suffit de récupérer l'heure à partir d'un TimePicker ou autre :

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

}

0 votes

J'ai essayé mais souvent cela ne fonctionne pas comme prévu, il est donc préférable d'utiliser AlarmManager

0 votes

Certainement, si l'heure doit être exacte developer.Android.com/guide/background

1 votes

De nos jours, le gestionnaire d'alarme n'est pas renvoyé, pour des raisons de fond, et cette réponse est parfaite,

2voto

Uzair Points 957

Vous pouvez utiliser AndroidJob depuis 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 ))
     }
   }
 }

et un créateur de notification

  class NotificationJobCreator : JobCreator {

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

puis lancez dans votre classe d'application

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

La dépendance de gradle est

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X