Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scheduled job executes multiple time in Evernote- AndroidJob

I have one periodic job I want to run and it is implemented with the help of Evernote's Android Job library.

What I wish to achieve is to updateMyLocation ever 15 mins.

The problem is that every 15 mins, the job seems to be executing multiple times.

I tested using a OnePlus3 device and from debugging, I observed that the LocationUpdateJob.schedule() is called only once, which is correct, but the LocationUpdateJob.onRunJob() gets called multiple times, which is incorrect, but should only be called once every 15 mins.

Additionally, illegalStateExceptions are thrown from some devices, according to crashlytics. This particular exception only happens on Android 7 devices.

Here is the crash from crash report:

    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)

Here is my code :

Applicataion class

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

MainActivity :

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

LocationUpdateJob

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

I forked the Evernote's sample project, but they do the exact same steps, but I couldn't figure out what I am doing differently.

like image 865
rgv Avatar asked Sep 11 '17 18:09

rgv


2 Answers

I found the answer to the whole thing. I am answering this, incase anyone else faces the same issue.

Ok, so according to my logic above, this is what was happening :

1) User opens the app for very first time, a job is scheduled in the OS, which runs till infinity.

2) User closes and opens the app again, another job is scheduled in the OS.

3) By the 101th time user opens the app, there are now 100 jobs scheduled and the app goes to schedule the 101th job, which throws an exception, because android only allows an app to schedule 100 jobs(in newer OS versions).

So, what did I do to solve this ?

I modified my schedule() to look like the following :

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

What is different here, from the old way I scheduled is : .setUpdateCurrent(true)

So what that does is, every time a job is scheduled with a tag, it replaces any existing jobs with the same tag, with the new job, therefore only one job is scheduled to execute with that tag, ie,the job's Tag is made unique.

There is a very brief and good explanation here, please read it.

like image 156
rgv Avatar answered Nov 05 '22 03:11

rgv


In case of WorkManager, I have added .beginUniqueWork() before .enqueue() and it worked for me.

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

The below discussion helped me to find the solution.

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

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

like image 1
Arunendra Avatar answered Nov 05 '22 02:11

Arunendra