Планировщик заданий не работает на Android N

Планировщик заданий работает как ожидается на устройствах Android Marshmallow и Lollipop, но он не работает и Nexus 5x (Android N Preview).

Код для планирования задания

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

Сервис определяется в манифесте как:

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

Есть ли у вас такая проблема на Android N (Preview)?

Ответ 1

В Android Nougat вызов setPeriodic(long intervalMillis) использует setPeriodic (long intervalMillis, long flexMillis), чтобы планировать периодические задания.

В соответствии с документацией:

JobInfo.Builder setPeriodic (long intervalMillis, long flexMillis)

Укажите, что это задание должно повторяться с заданным интервалом и прогибается. Задание может выполняться в любое время в окне длины гибки при конец периода.

intervalMillis long:    Миллисекундный интервал, для которого эта работа будет повторяться. Минимальное значение getMinPeriodMillis() принудительно.

flexMillis long:    Millisecond flex для этой работы. Flex зажимается как минимум getMinFlexMillis() или 5 процентов от периода, в зависимости от того, что больше.

Пример периодического задания, запланированного на 5 секунд:

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

Вышеприведенный код хорошо работает в Lollipop и Marshmallow, но когда вы запустите в Nougat, вы увидите следующий журнал:

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

Так как мы установили периодический интервал обновления до 5 секунд, который меньше порога getMinPeriodMillis(). Android Nougat обеспечивает getMinPeriodMillis().

В качестве обходного пути я использую следующий код для планирования заданий с периодическими интервалами, если интервал работы меньше 15 минут.

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

Пример примера 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);
    }
}

Ответ 2

Я нашел ответ на вопрос, с которым мы сталкиваемся с устройствами Nougat. Устройства Nougat не могут планировать задание, если задание необходимо перепланировать менее 15 минут.

Я пробовал, предоставляя время Интервала как 15 минут, и задание начинало получать по расписанию каждые 15 минут.

Код планировщика заданий:

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

Служба работы:

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

Короче, если бы вы увеличили время интервала до 15 минут, код начал бы работать.

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

Ответ 3

Ниже приведено обходное решение для >= Android N (Если вы хотите установить периодическое задание ниже 15 минут) Убедитесь, что используется только setMinimumLatency. Кроме того, если вы выполняете задание, которое занимает много времени, следующее задание будет запланировано, Текущее время выполнения задачи + PROVIDED_TIME_INTERVAL

.SetPeriodic(long millis) хорошо работает для уровня API ниже Android N

@Override
public boolean onStartJob(final JobParameters jobParameters) {
    Log.d(TAG,"Running service now..");
    //Small or Long Running task with callback

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

    //Call Job Finished
    jobFinished(jobParameters, false );

    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(), 
                    YourJobService.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);
  }


  if (mJobScheduler != null && mJobScheduler.schedule(mJobBuilder.build()) 
                <= JobScheduler.RESULT_FAILURE) {
     //Scheduled Failed/LOG or run fail safe measures
     Log.d(TAG, "Unable to schedule the service!");
  }
}

Ответ 4

если вы хотите периодически запускать код менее 15 минут, вы можете использовать сложный способ. Установите jobFinished() следующим образом

jobFinished(parameters, true);

он перепланирует код с помощью стратегии повтора. Определите пользовательские критерии отсрочки, используя

.setBackoffCriteria();

в построителе. Затем он будет периодически запускаться