47 votes

Le Job Scheduler ne fonctionne pas sur Android N

Le planificateur de tâches fonctionne comme prévu sur les appareils Android Marshmallow et Lollipop, mais il ne fonctionne pas sur le Nexus 5x (Android N Preview).

Code pour la programmation du travail

        ComponentName componentName = new ComponentName(MainActivity.this, TestJobService.class.getName());
        JobInfo.Builder builder;
        builder = new JobInfo.Builder(JOB_ID, componentName);
        builder.setPeriodic(5000);
        JobInfo jobInfo;
        jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobInfo = builder.build();
        int jobId = jobScheduler.schedule(jobInfo);

Le service est défini dans le manifeste comme :

<service android:name=".TestJobService"
            android:permission="android.permission.BIND_JOB_SERVICE" />

Est-ce que quelqu'un a ce problème sur Android N (Preview) ?

0 votes

Où essayez-vous de trouver un emploi ? Je veux dire, c'est un récepteur de diffusion ou quelque chose comme ça ?

0 votes

@rom4ek c'est dans l'activité pour le moment, car c'est un exemple d'application.

60voto

blizzard Points 3033

Dans Android Nougat, le setPeriodic(long intervalMillis) fait appel à la méthode setPeriodic (long intervalMillis, long flexMillis) pour planifier des travaux périodiques.

Conformément à la documentation :

JobInfo.Builder setPeriodic (long intervalMillis, long flexMillis)

Indiquez que cette tâche doit se répéter avec l'intervalle et le paramètre flex. Le travail peut s'exécuter à tout moment dans une fenêtre de longueur flex à la la fin de la période.

intervalleMillis long : Intervalle en millisecondes pendant lequel cette tâche sera répétée. Une valeur minimale de getMinPeriodMillis() est appliquée.

flexMillis long : Flex en millisecondes pour ce travail. Le flex est fixé à au moins getMinFlexMillis() ou 5 % de la période, la valeur la plus élevée étant retenue.

Exemple de travail périodique programmé pour 5 secondes :

private static final int JOB_ID = 1001;
private static final long REFRESH_INTERVAL  = 5 * 1000; // 5 seconds

JobInfo jobInfo = new JobInfo.Builder(JOB_ID, serviceName)
        .setPeriodic(REFRESH_INTERVAL)
        .setExtras(bundle).build();

Le code ci-dessus fonctionne bien dans Lollipop & Marshmallow mais lorsque vous l'exécutez dans Nougat, vous remarquerez le log suivant :

W/JobInfo: Specified interval for 1001 is +5s0ms. Clamped to +15m0s0ms
W/JobInfo: Specified flex for 1001 is +5s0ms. Clamped to +5m0s0ms

Puisque nous avons fixé l'intervalle de rafraîchissement périodique à 5 secondes, ce qui est inférieur au seuil fixé. getMinPeriodMillis() . Android Nougat applique le getMinPeriodMillis() .

Comme solution de rechange, j'utilise le code suivant pour planifier les tâches à intervalles périodiques si l'intervalle des tâches est inférieur à 15 minutes.

JobInfo jobInfo;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  jobInfo = new JobInfo.Builder(JOB_ID, serviceName)
      .setMinimumLatency(REFRESH_INTERVAL)
      .setExtras(bundle).build();
} else {
  jobInfo = new JobInfo.Builder(JOB_ID, serviceName)
      .setPeriodic(REFRESH_INTERVAL)
      .setExtras(bundle).build();
}

Exemple de service d'emploi :

public class SampleService extends JobService {
    @Override public boolean onStartJob(JobParameters params) {
        doSampleJob(params); 
        return true;
    }

    @Override public boolean onStopJob(JobParameters params) {
        return false;
    }

    public void doSampleJob(JobParameters params) {
        // Do some heavy operation
        ...... 
        // At the end inform job manager the status of the job.
        jobFinished(params, false);
    }
}

8 votes

Mais il semble ne fonctionner qu'une seule fois avec setMinimumLatency

1 votes

@KarthikRk, le travail ne peut pas être périodique et avoir une latence minimale, donc si vous pouvez définir la latence minimale, c'est un travail unique.

18 votes

Cela ne résout pas le problème... le travail ne sera pas exécuté périodiquement avec ce code.

18voto

MRah Points 670

Si quelqu'un essaie toujours de surmonter la situation,

Voici une solution de contournement pour >= Android N (si vous voulez définir la tâche périodique inférieure à 15 minutes)

Vérifiez que seul setMinimumLatency est utilisé. De plus, si vous exécutez une tâche qui prend beaucoup de temps, la tâche suivante sera programmée à, Current JOB Finish time + PROVIDED_TIME_INTERVAL.

.SetPeriodic(long millis) fonctionne bien pour les niveaux d'API inférieurs à Android N

@Override
public boolean onStartJob(final JobParameters jobParameters) {
    Log.d(TAG,"Running service now..");
    //Small or Long Running task with callback
    //Call Job Finished when your job is finished, in callback
    jobFinished(jobParameters, false );

    //Reschedule the Service before calling job finished
    if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
              scheduleRefresh();

    return true;
}

@Override
public boolean onStopJob(JobParameters jobParameters) {
    return false;
}

private void scheduleRefresh() {
  JobScheduler mJobScheduler = (JobScheduler)getApplicationContext()
                    .getSystemService(JOB_SCHEDULER_SERVICE);
  JobInfo.Builder mJobBuilder = 
  new JobInfo.Builder(YOUR_JOB_ID,
                    new ComponentName(getPackageName(), 
                    GetSessionService.class.getName()));

  /* For Android N and Upper Versions */
  if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
      mJobBuilder
                .setMinimumLatency(60*1000) //YOUR_TIME_INTERVAL
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
  }

UPDATE : Si vous envisagez d'exécuter votre travail répétitif pendant que vous êtes en Mode "Doze et de penser à JobScheduler, FYI : Les JobSchedulers ne sont pas autorisés à fonctionner en mode Doze.

Je n'ai pas parlé de Dozing car nous parlions de JobScheduler. Merci, @Elletlar Je vous remercie d'avoir souligné que certains pourraient penser que le programme fonctionne même lorsque l'application est en mode veille, ce qui n'est pas le cas.

Pour le mode somnolence, AlarmManager reste la meilleure solution. Vous pouvez utiliser setExactAndAllowWhileIdle() si vous voulez exécuter votre travail périodique à une période exacte ou utiliser la fonction setAndAllowWhileIdle() si vous êtes flexible.

Vous pouvez également utiliser setAlarmClock() car l'appareil sort toujours du mode somnolence pour le réveil et revient à nouveau en mode somnolence. Une autre solution consiste à utiliser le FCM.

1 votes

Bonne solution de contournement, mes premiers tests montrent que cela fonctionne mieux que FCM.

0 votes

@MRah Je comprends la solution de contournement du calendrier, mais pourquoi retournez-vous true et non false à partir de onStartJob ? Cela laisse le travail dans un état "inachevé", n'est-ce pas ?

0 votes

@dor506, si vous effectuez une tâche de longue durée et que vous renvoyez false, alors onStopJob est appelé et votre tâche ne sera pas terminée. Donc vous retournez true et quand votre tâche est terminée vous appelez JobFinished(params,false) pour libérer wakelock et arrêter la tâche. J'ai modifié la réponse, en espérant qu'elle sera plus compréhensible.

12voto

J'ai trouvé la réponse au problème que nous rencontrons avec les appareils Nougat. Les appareils Nougat ne sont pas en mesure de planifier le travail si le travail doit être reprogrammé moins de 15 minutes.

J'ai essayé en donnant un intervalle de 15 minutes et la tâche a commencé à être planifiée toutes les 15 minutes.

Code du planificateur d'emploi :

public static void scheduleJob(Context context) {
    ComponentName serviceComponent = new ComponentName(context, PAJobService.class);
    JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, serviceComponent);
    builder.setPeriodic(15 * 60 * 1000, 5 * 60 *1000);

    JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
    int ret = jobScheduler.schedule(builder.build());
    if (ret == JobScheduler.RESULT_SUCCESS) {
        Log.d(TAG, "Job scheduled successfully");
    } else {
        Log.d(TAG, "Job scheduling failed");
    }
}

Service de l'emploi :

public class PAJobService extends JobService {
    private static final String TAG = PRE_TAG + PAJobService.class.getSimpleName();
    private LocationManager mLocationManager;

    public boolean onStartJob(JobParameters params) {
        Log.d(TAG, "onStartJob");
        Toast.makeText(getApplicationContext(), "Job Started", Toast.LENGTH_SHORT).show();
        return false;
    }

    public boolean onStopJob(JobParameters params) {
        Log.d(TAG, "onStopJob");
        return false;
    }
}

En bref, si vous aviez augmenté la durée de l'intervalle à 15 minutes, le code aurait commencé à fonctionner.

private static final long REFRESH_INTERVAL = 15 * 60 * 1000;

1voto

MarGin Points 568

Si vous voulez exécuter le code périodiquement en moins de 15 minutes, vous pouvez utiliser une méthode plus délicate. Définissez votre jobFinished() comme ceci

jobFinished(parameters, true);

il reprogrammera le code avec une stratégie de réessai. Définissez un critère de temporisation personnalisé en utilisant

.setBackoffCriteria();

dans le constructeur. Ensuite, il s'exécutera périodiquement

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