Планировщик заданий не работает на 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 Н (превью)?

4 ответов


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

согласно документации:

JobInfo.Builder setPeriodic (длинные интервалмиллисы, длинные флексмиллисы)

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

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

flexMillis long: Миллисекундный сгиб для этой работы. Шлейф зажимается по крайней мере 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, но при запуске в Нуге вы заметите следующий журнал:

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 нуга обеспечивает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);
    }
}

Если кто-то все еще пытается преодолеть ситуацию,

вот обходной путь для > = 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(), 
                    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);
  }

обновление: Если вы рассматриваете повторное задание для запуска в Режим Дремать и думая о JobScheduler,FYI: JobSchedulers не разрешается работать в режиме Doze.

Я не обсуждал о дремоте, потому что мы говорили о JobScheduler. Спасибо, @Elletlar, для указания на то, что некоторые могут подумать, что он будет работать, даже если приложение находится в режиме дремоты, что не так.

для режима doze AlarmManager по-прежнему дает лучшее решение. можно использовать setExactAndAllowWhileIdle () если вы хотите запустить периодическую работу в точный период времени или использовать setAndAllowWhileIdle () если ты гибкий.

вы также можете user setAlarmClock () как устройство всегда выходит из режима doze для будильника и снова возвращается в режим doze. Другой способ-использовать FCM.


Я нашел ответ на вопрос, с которым мы сталкиваемся с устройствами нуги. Устройства 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;


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

jobFinished(parameters, true);

он перенесет код со стратегией повторной попытки. Определите пользовательские критерии возврата с помощью

.setBackoffCriteria();

в строителя. Тогда он будет работать периодически