23

Part of my question, how I can set up a job with less then 15 minutes interval in "Nougat", was answerd by "blizzard" in his answer here:
Job Scheduler not running on Android N
He explained the problem and suggested to use the following workaround:

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

However, using the suggested

  .setMinimumLatency(REFRESH_INTERVAL)    

just starts the job once;
but how do I get it periodic with a period of around 30 seconds on an android nougat device (not using handler or alarm manager)?

Community
  • 1
  • 1
tomseitz
  • 503
  • 2
  • 4
  • 14
  • 3
    Doing something every 30 seconds, except when your app is in the foreground and actively being used by the user, is inappropriate in the eyes of users, who have complained loudly about battery consumption by background apps. – CommonsWare Sep 22 '16 at 14:05
  • 1
    However, if you have users who wish to have the latest information available when they start their phone (maybe they are now without internet...), one needs a reliable background service. Or are you trying to tell me, that this is just plainly not possible? Then you could do so, without trying to teach me about things I know. – tomseitz Sep 22 '16 at 15:52
  • 1
    "if you have users who wish to have the latest information available when they start their phone" -- that does not require doing work every 30 seconds. "Or are you trying to tell me, that this is just plainly not possible?" -- there's nothing that will do this reliably, unless your app is in the foreground and is actively being used. – CommonsWare Sep 22 '16 at 15:57
  • 1
    as per the documentation, you can have either the setMinimumLatency() or setPeriodic(). Can't have both or else an exception will be thrown. https://developer.android.com/reference/android/app/job/JobInfo.Builder.html#setMinimumLatency(long) – Akshat Oct 11 '16 at 22:04
  • "if you have users who wish to have the latest information available **when they start their phone**" - sounds like you already figured out which event to use :) rather than continuously running in the background. – Tiago Espinha Apr 15 '17 at 09:36
  • 5
    I also want this to trigger every 20 or 30 seconds, but only while debugging. Waiting 15 minutes to see if your code works is a joke. Surely this should be an option in Android developer settings? – intrepidis Dec 10 '17 at 12:35
  • 4
    Waiting 15 mins for debugging is waste of time. Is there a Debug setting ? – Akshay Mathur Sep 15 '18 at 13:19
  • While I understand that it is usually a bad idea to pound the processor, there are always exceptions to the rule. Unfortunately I have a business case where needs to track the phone and specifically needs updates faster than 15 minutes. While I appreciate that this is supposed to protect users, in the end it also makes it a lot harder to do some legit applications. – MBentley Jul 10 '19 at 15:32
  • I have also the same issue, my job scheduler also not working working when i have closed my application. Can any body suggest me, what will be solution – Pradeep Kumar Oct 22 '19 at 10:42

2 Answers2

14

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

    //Reschedule the Service before calling job finished
    if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
              scheduleRefresh();

    //Call Job Finished
    jobFinished(jobParameters, false );

    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.

Reference: Doze Restrictions

https://developer.android.com/training/monitoring-device-state/doze-standby

MRah
  • 940
  • 6
  • 15
  • No discussion of *doze*? I find it difficult to believe that this solution will continue to work when the device is unplugged and the screen is turned OFF for SDK_INT >= Build.VERSION_CODES.N. – Elletlar Jul 23 '18 at 16:44
  • 1
    @Elletlar, I have not considered Doze mode when I answered this primarily because we are discussing about JobScheduler and in Doze mode JobSchedulers are restricted whether you set it lower or more than 15 mins. I use alarm manager if I must require to run a repeated service that frequently (including in doze mode). FCM can be used as well. – MRah Jul 23 '18 at 20:15
  • 1
    Thanks. I just thought it would have been good to mention that in the answer as many developers will only notice that behaviour after they've implemented it. I've been evaluating JobScheduler for one of my legacy apps, but I think setExactAndAllowWhileIdle in the AlarmManager still offers the best outcome for users that choose to set a frequent sync interval. – Elletlar Jul 24 '18 at 09:56
  • Yes, setExactAndAllowWhileIdle() if you are not flexible on the time and setAndAllowWhileIdle() if you're flexible offers the best outcome in doze mode. Btw, I am updating the answer as someone who has no idea about doze mode may not be following this discussion. – MRah Jul 24 '18 at 14:50
  • @Elletlar, updated the answer to include Doze mode. Thanks. – MRah Jul 24 '18 at 15:02
  • What source did you read that Job doesn't execute when Doze Mode ? – Hanako Aug 08 '18 at 20:25
  • @GianGomen updated the answer and included dev documentation for doze. – MRah Aug 08 '18 at 20:32
  • Thank you.But it says that despite Doze mode blocks alarms, jobs,etc. It periodically runs a maintain method that allows pending works to execute. So I woudn't say that jobs are not executed when doze mode. "Periodically, the system exits Doze for a brief time to let apps complete their deferred activities. During this maintenance window, the system runs all pending syncs, jobs, and alarms, and lets apps access the network." https://developer.android.com/training/monitoring-device-state/doze-standby#understand_doze – Hanako Aug 08 '18 at 21:21
  • I use AlarmManager, but after approximately 2 hours (every minute showing notification) don't execute anymore. `alarmMgr = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this, NotificationBroadcastReceiver.class); PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 1, intent, 0); alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + INTERVAL_ONE_MINUTE, INTERVAL_ONE_MINUTE, alarmIntent); Log.d("Alarm", "Started");` – Valentin Gjorgoski Aug 26 '19 at 10:43
  • @ValentinGjorgoski, did you check if your service is getting killed in any way after two hours? It's hard to find without reviewing the code. – MRah Sep 04 '19 at 17:16
  • @MRah Yes, service is killed, but I started service with AlarmManager. I think that AlarmManager need to start again. I tested that when service is started I force stop the service and it is started again, but why if OS kill service AlarmManager don't start. And that is on Oreo. I tested on 7.0 version and work good. Maybe problem is with foreground service... – Valentin Gjorgoski Sep 05 '19 at 06:43
0

I struggled with same thing when I wanted to setup Job for refresh small part of data. I found out that solution for this problem may be setting up Job one more time with the same ID after i calledjobFinished(JobParameters, boolean). I think it should work every time on main thread.

My function to setup Job looks like this:

JobInfo generateRefreshTokenJobInfo(long periodTime){
    JobInfo.Builder jobBuilder = new JobInfo.Builder(1L, new ComponentName(mContext, JobService.class));
    jobBuilder.setMinimumLatency(periodTime);
    jobBuilder.setOverrideDeadline((long)(periodTime * 1.05));
    jobBuilder.setRequiresDeviceIdle(false);
    return jobBuilder.build();
}

When I finish my work after first call of Job i call in main thread

jobFinished(mJobParameters, true);
registerRefreshJob(5*60*1000L);

This will reschedule my Job one more time for the same amount of time on the same id. When device is in idle state you still have to take under consideration lack of wake locks in doze so your job may not be started so often as you wish. It is mentioned in https://developer.android.com/about/versions/nougat/android-7.0-changes.html

If the device is stationary for a certain time after entering Doze, the system applies the rest of the Doze restrictions to PowerManager.WakeLock, AlarmManager alarms, GPS, and Wi-Fi scans. Regardless of whether some or all Doze restrictions are being applied, the system wakes the device for brief maintenance windows, during which applications are allowed network access and can execute any deferred jobs/syncs.

Changeling
  • 19
  • 2
  • 3
    Beware though, the jobscheduler times out in 1 minute. If your task takes more than a minute, the `jobFinished` will not run. `onStopJob` is called after the timeout. So if I were you, I would also reregister inside `onStopJob` as well. – Thupten Jan 15 '17 at 02:41
  • 1
    the limit of 1 min is on android 5.1.1 – Thupten Jan 15 '17 at 03:07