Запланированное задание выполняется несколько раз в Evernote — AndroidJob

У меня есть одно периодическое задание, которое я хочу запустить, и оно реализовано с помощью библиотеки заданий Evernote для Android.

Чего я хочу добиться, так это обновлять MyLocation каждые 15 минут.

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

Я тестировал устройство OnePlus3 и при отладке заметил, что LocationUpdateJob.schedule() вызывается только один раз, что правильно, а LocationUpdateJob.onRunJob() вызывается несколько раз, что неверно, но его следует вызывать только один раз каждые 15 минут.

Кроме того, по данным crashlytics, с некоторых устройств выбрасываются нелегальные исключения StateException. Это конкретное исключение происходит только на устройствах Android 7.

Вот сбой из отчета о сбое:

    Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mydomain.myapp/MainActivity}: java.lang.IllegalStateException: Apps may not schedule more than 100 distinct jobs
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2947)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3008)
       at android.app.ActivityThread.-wrap14(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6688)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)
Caused by java.lang.IllegalStateException: Apps may not schedule more than 100 distinct jobs
       at android.os.Parcel.readException(Parcel.java:1701)
       at android.os.Parcel.readException(Parcel.java:1646)
       at android.app.job.IJobScheduler$Stub$Proxy.schedule(IJobScheduler.java:158)
       at android.app.JobSchedulerImpl.schedule(JobSchedulerImpl.java:42)
       at com.evernote.android.job.v21.JobProxy21.schedule(SourceFile:198)
       at com.evernote.android.job.v21.JobProxy21.plantPeriodic(SourceFile:92)
       at com.evernote.android.job.JobManager.scheduleWithApi(SourceFile:282)
       at com.evernote.android.job.JobManager.schedule(SourceFile:240)
       at com.evernote.android.job.JobRequest.schedule(SourceFile:366)
       at com.mydomain.myapp.service.locationUpdate.LocationUpdateJob.schedule(SourceFile:33)
       at com.mydomain.myapp.activities.HubActivity.onLoginSuccess(SourceFile:173)
       at com.mydomain.myapp.activities.HubActivity.onCreate(SourceFile:115)
       at android.app.Activity.performCreate(Activity.java:6912)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2900)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3008)
       at android.app.ActivityThread.-wrap14(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6688)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

Вот мой код:

Класс приложения

@Override
public void onCreate() {
 //init things....
 JobManager
           .create(this)
           .addJobCreator(new LocationUpdateJobCreator());  
 }

LocationUpdateJobCreator

public class LocationUpdateJobCreator implements JobCreator {

    @Override
    public Job create(String s) {
        switch (s) {
            case LocationUpdateJob.TAG:
                return new LocationUpdateJob();
            default:
                return null;
        }
    }
}

Основная активность:

private void onLogin() {
   // do other things...
   LocationUpdateJob.schedule();
 }

Обновитьместоположение

public class LocationUpdateJob extends Job {

    public static final String TAG = "LocationUpdateJob";
    private static int jobId = -1;


    public static void schedule() {
        final long INTERVAL = 900000L;
        final long FLEX = 300000L;
        jobId = new JobRequest
                .Builder(LocationUpdateJob.TAG)
                .setPeriodic(INTERVAL, FLEX)
                .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
                .build()
                .schedule();
    }


    public static void stop() {
        JobManager
                .instance()
                .cancel(jobId);
    }


    @NonNull
    @Override
    protected Result onRunJob(Params params) {
        updateLocation();
        return Result.SUCCESS;
    }
}

Я разветвил образец проекта Evernote, но они делают те же самые шаги, но я не мог понять, что я делаю по-другому.


person rgv    schedule 11.09.2017    source источник
comment
Такая же проблема, stackoverflow.com/q/50877452/6352712   -  person Serg Burlaka    schedule 15.06.2018


Ответы (2)


Я нашел ответ на все вопросы. Я отвечаю на это, если кто-то еще сталкивается с той же проблемой.

Итак, согласно моей логике выше, это то, что происходило:

1) Пользователь открывает приложение в первый раз, в ОС запланировано задание, которое выполняется до бесконечности.

2) Пользователь закрывает и снова открывает приложение, в ОС запланировано другое задание.

3) К 101-му разу, когда пользователь открывает приложение, теперь запланировано 100 заданий, и приложение переходит к планированию 101-го задания, что вызывает исключение, потому что Android позволяет приложению планировать только 100 заданий (в более новых версиях ОС).

Итак, что я сделал, чтобы решить эту проблему?

Я изменил свой schedule(), чтобы он выглядел следующим образом:

public static void schedule() {
     final long INTERVAL = 900000L;
     final long FLEX = 300000L;
     jobId = new JobRequest
                    .Builder(LocationUpdateJob.TAG)
                    .setPeriodic(INTERVAL, 300000L)
                    .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
                    .setPersisted(true)
                    .setUpdateCurrent(true)
                    .build()
                    .schedule();
}

Что здесь отличается от старого способа, который я запланировал, так это: .setUpdateCurrent(true)

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

здесь есть очень краткое и хорошее объяснение, прочтите его.

person rgv    schedule 11.09.2017
comment
Есть только один побочный эффект setUpdateCurrent(true), который у меня не работает: он перепланирует новое задание, начиная с этого момента. Итак, если, скажем, ваша повторяющаяся задача выполняется каждые 24 часа, но приложение постоянно прерывается пользователем два раза в день, повторно запланированная задача никогда не будет выполняться. Поэтому я решил эту проблему с помощью строгой защиты для того же запланированного задания, используя JobManager.instance().getAllJobRequestsForTag(tag), а в противном случае использовал setUpdateCurrent(true). - person Alessio; 17.03.2018
comment
Это обсуждение мне очень помогло .. +1 - person BENN1TH; 30.04.2018
comment
можно ли запускать обе работы в хронологическом порядке? у меня есть этот вариант использования, когда мне нужно обновить данные на сервер в фоновом режиме через 60 секунд после транзакции (денежной транзакции). Итак, предположим, я делаю первую транзакцию и планирую выполнение задания через 60 секунд, а теперь в течение этих 60 секунд я делаю еще одну транзакцию, и для этого я запланировал другое задание, но с тем же тегом. я обнаружил, что второй переопределяет первый, и это каким-то образом заставляет меня думать о любой функции постановки в очередь, которая должна быть встроена в эту библиотеку, если ее нет. есть? - person Aman Verma; 07.06.2018
comment
Я думаю, что это должен быть отдельный вопрос, но причина, по которой он перегружен, заключается в том, что если вы создаете задания с одним и тем же тегом и setUpdate (true), он будет заменен - person rgv; 07.06.2018
comment
Всем привет, enqueueUniqueWork (из версии 1.0.0-alpha11) и enqueueUniquePeriodicWork (из версии 1.0.0-alpha03) уже доступны: stackoverflow. com/a/51083407/6086782 - person varun; 08.04.2019

В случае WorkManager я добавил .beginUniqueWork() перед .enqueue(), и у меня это сработало.

  Constraints myConstraints = new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build();
  Data.Builder basicWorkerInputDataBuilder = new Data.Builder();

  OneTimeWorkRequest otwRequest = new OneTimeWorkRequest.Builder(BasicOneTimeWorker.class)                  
                .setConstraints(myConstraints).addTag("BasicOneTimeWorker")                    
                .setInputData(basicWorkerInputDataBuilder.build());

  WorkManager.getInstance().beginUniqueWork("uniqueBasicOneTimeWork",
                                            ExistingWorkPolicy.REPLACE,
                                           otwRequest).enqueue();

Приведенное ниже обсуждение помогло мне найти решение.

https://github.com/googlecodelabs/android-workmanager/issues/16

https://issuetracker.google.com/issues/111569265

person Arunendra    schedule 13.03.2019