Job Scheduler not running on Android N

35,795

Solution 1

In Android Nougat the setPeriodic(long intervalMillis) method call makes use of setPeriodic (long intervalMillis, long flexMillis) to schedule periodic jobs.

As per the documentation:

JobInfo.Builder setPeriodic (long intervalMillis, long flexMillis)

Specify that this job should recur with the provided interval and flex. The job can execute at any time in a window of flex length at the end of the period.

intervalMillis long: Millisecond interval for which this job will repeat. A minimum value of getMinPeriodMillis() is enforced.

flexMillis long: Millisecond flex for this job. Flex is clamped to be at least getMinFlexMillis() or 5 percent of the period, whichever is higher.

Sample periodic job scheduled for 5 seconds:

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

The above code works well in Lollipop & Marshmallow but when you run in Nougat you will notice the following log:

W/JobInfo: Specified interval for 1001 is +5s0ms. Clamped to +15m0s0ms
W/JobInfo: Specified flex for 1001 is +5s0ms. Clamped to +5m0s0ms

Since we have set the periodic refresh interval to 5 seconds which is less than the thresholdgetMinPeriodMillis(). Android Nougat enforces the getMinPeriodMillis().

As a workaround, I'm using following code to schedule jobs at periodic intervals if job interval is less than 15minutes.

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

Sample JobService example:

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

Solution 2

If someone is still trying to overcome the situation,

Here is a workaround for >= Android N (If you want to set the periodic job lower than 15 minutes)

Check that only setMinimumLatency is used. Also, If you are running a task that takes a long time, the next job will be scheduled at, Current JOB Finish time + PROVIDED_TIME_INTERVAL

.SetPeriodic(long millis) works well for API Level below 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);
  }

UPDATE: If you are considering your repeating job to run while in Doze Mode and thinking about JobScheduler, FYI: JobSchedulers are not allowed to run in Doze mode.

I have not discussed about the Dozing because we were talking about JobScheduler. Thanks, @Elletlar, for pointing out that some may think that it will run even when the app is in doze mode which is not the case.

For doze mode, AlarmManager still gives the best solution. You can use setExactAndAllowWhileIdle() if you want to run your periodic job at exact time period or use setAndAllowWhileIdle() if you're flexible.

You can also user setAlarmClock() as device always comes out from doze mode for alarm clock and returns to doze mode again. Another way is to use FCM.

Solution 3

I have found the answer to the issue that we are facing with Nougat devices. Nougat devices are not able to schedule the job if the job needs to be re-scheduled lesser than 15 minutes.

I tried out by giving the Interval time as 15 minutes and the job started getting scheduled every 15 minutes.

Job Scheduler Code:

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

Job Service:

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

In short if you would have increased the Interval Time to 15 minutes the code would have started working.

private static final long REFRESH_INTERVAL = 15 * 60 * 1000;

Solution 4

if you want to run the code periodically less than 15 minutes then you can use a tricky way. Set your jobFinished() like this

jobFinished(parameters, true);

it will reschedule the code with a retry strategy. Define a custom backoff criteria using

.setBackoffCriteria();

in the builder. Then it will run periodically

Share:
35,795

Related videos on Youtube

Adil Bhatty
Author by

Adil Bhatty

droid guy

Updated on October 24, 2020

Comments

  • Adil Bhatty
    Adil Bhatty over 3 years

    Job Scheduler is working as expected on Android Marshmallow and Lollipop devices, but it is not running and Nexus 5x (Android N Preview).

    Code for scheduling the job

            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 is defined in manifest as:

    <service android:name=".TestJobService"
                android:permission="android.permission.BIND_JOB_SERVICE" />
    

    Is any one having this issue on Android N (Preview)?

    • romtsn
      romtsn almost 8 years
      Where do you try to schedule a job? I mean is it a broadcast receiver or something?
    • Adil Bhatty
      Adil Bhatty almost 8 years
      @rom4ek its in the activity at the moment, as its a sample app.
  • Karthik Rk
    Karthik Rk over 7 years
    but it seems to run only once with setMinimumLatency
  • DataDino
    DataDino over 7 years
    @KarthikRk, the Job can't be periodic and have a minimumLatency, so if you can set minimum latency the it is a one shot job.
  • forresthopkinsa
    forresthopkinsa almost 7 years
    This doesn't solve the problem... the job won't run periodically with this code.
  • Nandhan Thiravia
    Nandhan Thiravia almost 7 years
    Even I have the same problem, I am trying out ways but not possible to get it wwork
  • Badhrinath Canessane
    Badhrinath Canessane over 6 years
    setMinimumLatency is for specifying that this job should be delayed by the provided amount of time. Because it doesn't make sense setting this property on a periodic job, doing so will throw an IllegalArgumentException when build() is called.
  • Jay Dangar
    Jay Dangar almost 6 years
    same issue called only once.
  • Chud37
    Chud37 almost 6 years
    Has anyone been able to set a periodic job on Nougat?? I have just run in into this same problem and I am not getting anywhere. Works fine in Oreo.
  • androidneil
    androidneil over 5 years
    Good workaround, my initial tests show that this is working better than FCM.
  • MRah
    MRah over 5 years
    @JayDangar did you try the solution given below, I am not sure how this answer is still the accepted solution?
  • Jay Dangar
    Jay Dangar over 5 years
    @MRah, I have tried this solution on my device, but it still runs the jon only once and not periodically. Also as JobScheduler wont work for API level < 21, I have to shift from JobSchedulers to github.com/evernote/android-job this library, which works perfectly for me.
  • dor506
    dor506 over 5 years
    @MRah I understand the schedule workaround, but why do you return true and not false from onStartJob? This leaves the job in "unfinished" state, doesn't it?
  • MRah
    MRah over 5 years
    @dor506, if you're doing a long-running task and return false then onStopJob is called and your task will not be done. So you return true and when your job is finished you call JobFinished(params,false) to release wakelock and stop the job. I edited the answer, hopefully, it will be more understandable.
  • dor506
    dor506 over 5 years
    @MRah In my case I don't need long running job, just a simple if statement. In this case I should call jobFinished -> schedule (if needed) -> return false. Right?
  • Lonie
    Lonie over 5 years
    help a lot. but I m very curious about that why google recommend JobScheduler if it doesn't work fine with setPeriodic .
  • mehdi amirsardari
    mehdi amirsardari over 4 years
    onstop job not call after cancel all method, whats the solution ?
  • Michaël Randria
    Michaël Randria almost 3 years
    Setting the REFRESH_INTERVAL to 15 * 60 * 1000 was really helpfull! Thank you!