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

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

Код для планирования работы

        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 (предварительная версия)?


person kaibuki    schedule 13.07.2016    source источник
comment
Где вы пытаетесь планировать работу? Я имею в виду, это широковещательный приемник или что-то в этом роде?   -  person romtsn    schedule 13.07.2016
comment
@rom4ek в данный момент находится в работе, так как это пример приложения.   -  person kaibuki    schedule 13.07.2016


Ответы (4)


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

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

JobInfo.Builder setPeriodic (длинный интервалMillis, длинный flexMillis)

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

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

flexMillis long: flexMillis для этой работы. 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);
    }
}
person blizzard    schedule 04.08.2016
comment
но, похоже, он запускается только один раз с setMinimumLatency - person Karthik Rk; 01.10.2016
comment
@KarthikRk, задание не может быть периодическим и иметь минимальную задержку, поэтому, если вы можете установить минимальную задержку, это будет одноразовое задание. - person DataDino; 13.10.2016
comment
Это не решает проблему... задание не будет выполняться периодически с этим кодом. - person forresthopkinsa; 29.06.2017
comment
Даже у меня та же проблема, я пробую способы, но не могу заставить ее работать - person Nandhan Thiravia; 15.09.2017
comment
setMinimumLatency предназначен для указания того, что это задание должно быть отложено на указанное время. Поскольку не имеет смысла задавать это свойство для периодического задания, это вызовет исключение IllegalArgumentException при вызове build(). - person Badhrinath Canessane; 25.10.2017
comment
такая же проблема вызывалась только один раз. - person Jay Dangar; 14.09.2018
comment
Кто-нибудь смог поставить периодическую работу на Nougat?? Я только что столкнулся с этой же проблемой, и я никуда не денусь. В Орео работает нормально. - person Chud37; 21.09.2018
comment
@JayDangar, вы пробовали решение, приведенное ниже, я не уверен, почему этот ответ все еще является принятым решением? - person MRah; 04.10.2018
comment
@MRah, я пробовал это решение на своем устройстве, но оно по-прежнему запускает jon только один раз, а не периодически. Также, поскольку JobScheduler не будет работать для уровня API ‹ 21, мне придется перейти с JobSchedulers на github.com/evernote/ android-job эту библиотеку, которая у меня отлично работает. - person Jay Dangar; 04.10.2018
comment
onstop job не вызывается после отмены всех методов, каково решение? - person mehdi amirsardari; 14.10.2019

Если кто-то все еще пытается выйти из ситуации,

Вот обходной путь для >= Android N (если вы хотите установить периодическое задание менее 15 минут)

Убедитесь, что используется только setMinimumLatency. Кроме того, если вы выполняете задачу, которая занимает много времени, следующее задание будет запланировано на время завершения текущего задания + PROVIDED_TIME_INTERVAL.

.SetPeriodic(длинные миллисекунды) хорошо работает для уровня API ниже 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);
  }

ОБНОВЛЕНИЕ: если вы планируете запустить повторяющееся задание в режиме дремоты и думаете о JobScheduler, к вашему сведению: программам JobSchedulers не разрешено работать в режиме дремоты.< /сильный>

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

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

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

person MRah    schedule 11.12.2017
comment
Хороший обходной путь, мои первоначальные тесты показывают, что он работает лучше, чем FCM. - person androidneil; 03.10.2018
comment
@MRah Я понимаю обходной путь расписания, но почему вы возвращаете true, а не false из onStartJob? Это оставляет работу в незавершенном состоянии, не так ли? - person dor506; 23.10.2018
comment
@ dor506 dor506, если вы выполняете длительную задачу и возвращаете false, то вызывается onStopJob, и ваша задача не будет выполнена. Таким образом, вы возвращаете true, и когда ваша работа завершена, вы вызываете JobFinished(params,false), чтобы освободить wakelock и остановить работу. Я отредактировал ответ, надеюсь, он будет более понятным. - person MRah; 23.10.2018
comment
@MRah В моем случае мне не нужна длительная работа, просто простой оператор if. В этом случае я должен вызвать jobFinished -> schedule (если нужно) -> return false. Верно? - person dor506; 25.10.2018
comment
помогите много. но мне очень любопытно, почему Google рекомендует JobScheduler, если он не работает нормально с setPeriodic . - person Lonie; 15.11.2018

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

person Nandhan Thiravia    schedule 15.09.2017

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

jobFinished(parameters, true);

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

.setBackoffCriteria();

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

person MarGin    schedule 19.01.2018