Scheduled job executes multiple time in Evernote- AndroidJob
Asked Answered
S

2

8

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.

Serialize answered 11/9, 2017 at 18:11 Comment(1)
S
16

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.

Serialize answered 11/9, 2017 at 20:1 Comment(5)
There's only one side effect of setUpdateCurrent(true) which doesn't work for me: it re-schedules the new job starting from now. So if say your recurrent job happens every 24 hours, but the app is killed twice a day constantly by the user, the re-scheduled job will never run. So I solved this issue with a strict guard for the same scheduled job using JobManager.instance().getAllJobRequestsForTag(tag), while using setUpdateCurrent(true) otherwise.Jolin
This discussion helped me a lot ..+1Linguistic
is it possible to run both the jobs chronologically? i have this use case where i have to update data to server in background 60 seconds after a Transaction(Monetary transaction). So suppose i make a first transaction and schedule a job to run after 60 seconds and now within these 60 seconds i make another transaction and for this i scheduled another job but with the same tag. i found out that the second override the first and it somehow makes me think of any enqueue functionality to be embedded in this Library if there is none. is there?Slurry
I think this should be a separate question, but, the reason why it's being over ridden is, if you crate the jobs with same tag and setUpdate(true), it will get replacedSerialize
Hey everyone, enqueueUniqueWork (from Version 1.0.0-alpha11) and enqueueUniquePeriodicWork (from Version 1.0.0-alpha03) are available now: https://mcmap.net/q/594924/-android-workmanager-api-for-running-daily-task-in-backgroundTreasurer
V
1

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

Valise answered 13/3, 2019 at 6:33 Comment(1)
The questions od not about WM but about JS.Nifty

© 2022 - 2024 — McMap. All rights reserved.