web-dev-qa-db-fra.com

Job Scheduler ne fonctionne pas sur Android N

Job Scheduler fonctionne comme prévu sur les appareils Android Marshmallow et Lollipop, mais ne fonctionne pas et Nexus 5x (Android N Preview). 

Code pour la planification 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 manifeste comme:

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

Quelqu'un at-il ce problème sur Android N (Preview)?

38
kaibuki

Dans Android Nougat, l'appel de méthode setPeriodic(long intervalMillis) utilise setPeriodic (long intervalMillis, long flexMillis) pour planifier des travaux périodiques.

Selon la documentation:

JobInfo.Builder setPeriodic (long intervalMillis, long flexMillis)

Indiquez que ce travail doit être récurrent avec l'intervalle fourni et fléchir. Le travail peut être exécuté à tout moment dans une fenêtre de longueur variable à la fin de la période.

intervalMillis long: Intervalle en millisecondes pendant lequel ce travail sera répété. Une valeur minimale de getMinPeriodMillis () est appliquée.

flexMillis long: Milliseconde flex pour ce travail. Flex est limité à au moins getMinFlexMillis () ou à 5% de la période, selon la valeur la plus élevée.

Exemple de travail périodique planifié pendant 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 exécutez Nougat, vous remarquerez le journal suivant:

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

Depuis que nous avons défini l'intervalle d'actualisation périodique à 5 secondes, ce qui est inférieur au seuilgetMinPeriodMillis(). Android Nougat applique la getMinPeriodMillis()

En guise de solution de contournement, j'utilise le code suivant pour planifier des 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 d’exemple de JobService:

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);
    }
}
57
blizzard

Si quelqu'un essaie encore de surmonter la situation,

Voici une solution de contournement pour> = Android N (Si vous souhaitez définir un travail périodique inférieur à 15 minutes)  

Vérifiez que seul setMinimumLatency est utilisé. De même, si vous exécutez une tâche qui prend beaucoup de temps, le prochain travail sera planifié à, Heure de fin du travail actuel + PROVIDED_TIME_INTERVAL

.SetPeriodic (long millis) fonctionne bien pour le niveau API inférieur à 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 en répétition en mode Doze et si vous pensez à JobScheduler, FYI: les JobSchedulers ne peuvent pas s'exécuter en mode Doze.  

Je n'ai pas discuté de Dozing parce que nous parlions de JobScheduler. Merci, @Elletlar , pour avoir signalé que certains pourraient penser qu'elle s'exécutera même lorsque l'application est en mode veille, ce qui n'est pas le cas. 

Pour le mode assoupi, AlarmManager fournit toujours la meilleure solution. Vous pouvez utiliser setExactAndAllowWhileIdle () si vous souhaitez exécuter votre travail périodique à une heure précise ou utiliser setAndAllowWhileIdle () si vous êtes flexible. 

Vous pouvez également utiliser setAlarmClock () , car le périphérique sort toujours du mode veille pour le réveil et revient en mode veille. Une autre façon consiste à utiliser FCM. 

12
MRah

J'ai trouvé la solution au problème auquel nous sommes confrontés avec les périphériques Nougat… .. Les périphériques Nougat ne sont pas en mesure de planifier le travail s'il doit être reprogrammé en moins de 15 minutes.

J'ai essayé en donnant un intervalle de temps de 15 minutes et le travail commençait à être planifié toutes les 15 minutes.

Code du planificateur de travaux:

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

Job Service:

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é le temps d'intervalle à 15 minutes, le code aurait commencé à fonctionner.

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

8
Nandhan Thiravia

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

jobFinished(parameters, true);

il replanifiera le code avec une stratégie de nouvelle tentative. Définir un critère de retrait personnalisé en utilisant

.setBackoffCriteria();

dans le constructeur. Ensuite, il fonctionnera périodiquement

0
MarGin