Android JobScheduler выполняется несколько раз

Я использую приведенный ниже код для создания и планирования задания с использованием Androids JobScheduler API:

Log.d("hanif", "Scheduling periodic job 2 hrs with 20 mins backoff linear");

JobInfo jobInfo = new JobInfo.Builder(JOB_ID,
            new ComponentName(getApplicationContext(), MyJobService.class))
            .setPeriodic(TimeUnit.HOURS.toMillis(2))
            .setRequiresCharging(true)
            .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
            .setBackoffCriteria(TimeUnit.MINUTES.toMillis(20),
                                        JobInfo.BACKOFF_POLICY_LINEAR)
            .build();
JobScheduler scheduler = 
                (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo);

т.е. периодическое задание, выполняемое каждые 2 часа, и линейная отмена политики, которая выполняет 20 минут * номер, терпит неудачу в случае неудачи задания.

Мой код службы работы написан следующим образом:

public class MyJobService extends JobService {

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        Log.d("hanif", "onStartJob");
        new MyWorker(getApplicationContext(), this, jobParameters).execute();
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        Log.d("hanif", "onStopJob");
        return true;
    }

    private static class MyWorker extends AsyncTask<Void, Void, Boolean> {
        private final Context mContext;
        private final MyJobService mJobService;
        private final JobParameters mJobParams;

        public MyWorker(Context context, MyJobService myJobService, JobParameters jobParameters) {
            mContext = context;
            mJobService = myJobService;
            mJobParams = jobParameters;
        }

        @Override
        protected Boolean doInBackground(Void... voids) {
            Log.d("hanif", "Work start!");
            for (int i=0; i<999999999; i++) {}
            int counter = Prefs.getCounter(mContext);
            Log.d("hanif", "Work done! counter: " + counter);
            if (counter == 3) {
                Log.d("hanif", "DO RESCHEDULE");
                Prefs.resetCounter(mContext);
                return true;
            }
            Log.d("hanif", "DO NOT RESCHEDULE");
            Prefs.increaseCounter(mContext);
            return false;
        }

        @Override
        public void onPostExecute(Boolean reschedule) {
            if (reschedule) {
                mJobService.jobFinished(mJobParams, true);
            } else {
                mJobService.jobFinished(mJobParams, false);
            }
            Log.d("hanif", "------------------------------------------");
        }
    }

}

И, наконец, выход журнала выглядит следующим образом:

03-27 12:57:11.677  7383  7383 D hanif   : Scheduling periodic job 2 hrs with 20 mins backoff linear
03-27 12:57:31.904  7383  7383 D hanif   : onStartJob
03-27 12:57:31.909  7383  8623 D hanif   : Work start!
03-27 12:57:42.110  7383  8623 D hanif   : Work done! counter: 0
03-27 12:57:42.111  7383  8623 D hanif   : DO NOT RESCHEDULE
03-27 12:57:42.125  7383  7383 D hanif   : ------------------------
03-27 14:58:50.786  7383  7383 D hanif   : onStartJob
03-27 14:58:50.789  7383 21490 D hanif   : Work start!
03-27 14:59:00.952  7383 21490 D hanif   : Work done! counter: 1
03-27 14:59:00.953  7383 21490 D hanif   : DO NOT RESCHEDULE
03-27 14:59:00.962  7383  7383 D hanif   : ------------------------
03-27 16:57:12.021  7383  7383 D hanif   : onStartJob
03-27 16:57:12.045  7383 32028 D hanif   : Work start!
03-27 16:57:22.229  7383 32028 D hanif   : Work done! counter: 2
03-27 16:57:22.230  7383 32028 D hanif   : DO NOT RESCHEDULE
03-27 16:57:22.238  7383  7383 D hanif   : ------------------------
03-27 18:57:11.984  7383  7383 D hanif   : onStartJob
03-27 18:57:11.989  7383 13217 D hanif   : Work start!
03-27 18:57:22.123  7383 13217 D hanif   : Work done! counter: 3
03-27 18:57:22.124  7383 13217 D hanif   : DO RESCHEDULE
03-27 18:57:22.130  7383  7383 D hanif   : ------------------------
03-27 19:20:57.468  7383  7383 D hanif   : onStartJob
03-27 19:20:57.482  7383  1913 D hanif   : Work start!
03-27 19:21:07.723  7383  1913 D hanif   : Work done! counter: 0
03-27 19:21:07.724  7383  1913 D hanif   : DO NOT RESCHEDULE
03-27 19:21:07.733  7383  7383 D hanif   : ------------------------
03-27 19:21:57.669  7383  7383 D hanif   : onStartJob  <--- Why is this called again?
03-27 19:21:57.675  7383  3025 D hanif   : Work start!
03-27 19:22:07.895  7383  3025 D hanif   : Work done! counter: 1
03-27 19:22:07.896  7383  3025 D hanif   : DO NOT RESCHEDULE
03-27 19:22:07.906  7383  7383 D hanif   : ------------------------
03-27 21:40:53.419  7383  7383 D hanif   : onStartJob
03-27 21:40:53.423  7383 31526 D hanif   : Work start!
03-27 21:41:03.857  7383 31526 D hanif   : Work done! counter: 2
03-27 21:41:03.858  7383 31526 D hanif   : DO NOT RESCHEDULE
03-27 21:41:03.867  7383  7383 D hanif   : ------------------------

Почему onStartJob вызывается два раза?

Ответ 1

После долгих разочарований я понял, что вызывает эту проблему.

Вы должны не называть jobFinished(JobParameters, true) периодическое задание. Передача true для needsReschedule приведет к дублированию задания в очереди (вы ожидаете, что оно будет перезаписано исходным, но, по-видимому, это не так). Вы всегда должны использовать jobFinished(JobParameters, false), даже если ваша задача не работает.

Что касается того, считается ли это неправильным использованием API или ошибкой Android, я оставлю это до вас.

Ответ 2

Поскольку JobScheduler будет использоваться намного больше с Android Oreo, я хотел бы описать несколько проблем с примером:

Перегруженный метод onStopJob() возвращает только true. Если ваша работа прерывается во время ее обработки, то есть зарядное устройство отключено или нет сети, установленной в JobInfo, эту функцию следует использовать, чтобы сразу же отменить задачу/задание. Затем он должен возвращать true, указывающий, что задание необходимо перепланировать.

MyJobService должен иметь ссылку private для задания MyWorker. onStartJob() устанавливает его. onStopJob() использует его для немедленной отмены задачи.

Передача true для needsReschedule в рамках метода jobFinished() обычно не требуется, особенно когда вы вызываете ее в своей задаче. Если задание прерывается, onStopJob() будет вызываться, чтобы отменить его, а возврат true вернет его. Я видел только один пример здесь, где needsReschedule установлен на true. Он находится в пределах onStartJob(), и если двойная проверка предварительных условий не выполняется, jobFinished(args, true), а затем return false вместо true.

Надеюсь, это поможет!