Sunday, September 24, 2017

Android - Job Scheduler

Job Scheduler

Reason to use

  1. For background task when apps in not foreground, we usually prefer Service to perform the task.
  2. But if your app targets API level 26 or higher, the system imposes restrictions on running background services when the app is not in foreground.
  3. If you have works that need to be done periodically, you need to use Alarm.
For above, your app should use a scheduled job i.e JobScheduler.

What is Job Scheduler

  1. JobScheduler is guaranteed to get your job done, but since it operates at the system level, it can also use several factors to intelligently schedule your background work to run with the jobs from other apps.
  2. You can minimize the resource usage using JobScheduler. And as of API 24, JobScheduler even considers memory pressure, which is a clear overall win for devices and their users.
  3. By introducing JobScheduler at the system level, we can focus on batching similar work requests together, which results in a noticeable improvement for both battery and memory.
The great advantage to JobScheduler is that it doesn’t perform work solely based on time, but rather based on conditions.

How to use it

  1. JobScheduler has 3 distinct part
    1. JobService
      1. The task you want to do will be defined in class extending JobService.
      2. This enables the system to perform your work for you, regardless of whether you app is active or not.
      3. The most convenient part of this structure is that you can write multiple JobServices, where each one defines a different task that should be performed at some point for your app.
      4. This helps you modularize your code base, and make code maintenance much simpler.
      5. Here we have 3 methods in JobService to look
        1. @override boolean onStartJob (JobParameters params):
          1. It is called by the system when your job is to be executed.
          2. If your task is short, you can write the logic in this method itself else you have to run that code in another thread, as JobService runs in mainThread.
          3. When job done then return false else if starting another thread to perform task in that, return true, letting the system know that you have a thread still running and it should hold on to your wakelock for a while longer.
          4. Return:
            1. True: to tell system that work is not finished and to hold wakelock for longer
            2. False: to tell system work done.
        2. void jobFinished():
          1. This method is not to override, and the system will not call it.
          2. You need to call this method once your service or thread has finished working on the job.
          3. If you have started another thread and returned true from onStartJob(), you will need to call this method from that thread when work is complete.
          4. This is how the system knows that it can safely release your wakelock. If you forget to all jobFinished(), your app is going to look pretty guilty in the battery stats lineup.
          5. jobFinished() requires two parameters: the current job, so that it knows which wakelock can be released, and a boolean indicating whether you had like to reschedule the job. If you passing true, this will kick off the JobScheduler’s exponential backoff logic for you.
        3. @override onStopJob (JobParameters params):
          1. This method is called by the system if the job is cancelled before being finished.
          2. This generally happens when your job conditions are no longer being met, such as when the device has been unplugged or if WiFi is no longer available.
          3. So use this method for any safety checks and clean up resource in response of half-finished job.
          4. Then return true if you had like the system to reschedule the job.
          5. Return:
            1. True: to tell system to reschedule job.
            2. False: to tell system not to reschedule job.
    2. JobInfo:
      1. This is the object which will define the conditions based on which system will invoke your job.
      2. To build the JobInfo object, you need two things every time, a job number and your JobService.
      3. Criterias:
        1. Network type (metered/unmetered)
          1. If your job requires network access, you must include this condition.
          2. You can specify a metered or unmetered network or any type of network.
          3. If you do not use this condition, system will assume that your job does not need network.
        2. Charging and Idle
          1. If your app needs to do work that is resource intensive, it is highly recommended that you wait until the device is plugged in and/or idle.
        3. ContentProvider updates
          1. As of API 24, you can use a content provider change as a trigger to perform some work.
          2. You will need to specify the trigger URI, which will be monitored with a ContentObserver.
          3. You can also specify a delay before your job is triggered, if you want to be certain that all changes propagate before your job is run.
        4. BackOff criteria
          1. You can specify your own backoff/retry policy.
          2. This defaults to an exponential policy, but if you set your own and then return true for rescheduling a job in onStopJob(), the system will employ your specified policy over the default.
        5. Minimum latency & override deadline
          1. If your job can not start for at least X amount of time, specify minimum latency.
          2. If all conditions met but your minimum latency has not elapsed, your job will be held.
          3. If your job can not be delayed past a specific time, specify override deadline.
          4. Even if all conditions have not been met, your job will be run by the deadline.
          5. You can check the result of isOverrideDeadlineExpired() to determine if you are in that case.
        6. Periodic
          1. If you have work that needs to be done regularly, you can set up a periodic job,
          2. This is a great alternative to a repeating alarm, because you sort it all once, schedule it, and the job will run once in each specified period.
        7. Persistence
          1. Any work that needs to be persisted across a reboot can be marked as such here. Once the device reboots, the job will be rescheduled according to your conditions.( RECEIVE_BOOT_COMPLETED permission needed in manifest).
        8. Extras
          1. If your job needs some information from your app to perform its work, you can pass along primitive data types as extras in the JobInfo.
    3. JobParameters:
      1. Contains the parameters used to configure/identify your job. You do not create this object yourself, instead it is handed in to your application by the System.

  1. JobScheduler has 3 steps to complete the task
    1. Create task by extending JobService class
    2. Create JobInfo object
    3. Initialise and schedule job by using JobScheduler.

Code


public class DownloadJobSchedular {

  private static final int JOB_ID = 1;

  public static DownloadJobSchedular getInstance() {
      return new DownloadJobSchedular();
  }

 
private DownloadJobSchedular() {
  }

  @RequiresApi(api = VERSION_CODES.LOLLIPOP)
  public void schedule(final Context context) {
      JobScheduler jobScheduler =
              (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
      jobScheduler.schedule(new JobInfo.Builder(JOB_ID,
              new ComponentName(context, DownloadService.class))
              .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
              .build());
  }

  private static class DownloadTask extends AsyncTask<Void, Void, Boolean> {

      private final DownloadService mService;
      private final JobParameters mParams;

      public DownloadTask(final DownloadService service, final JobParameters params) {
          mService = service;
          mParams = params;
      }

      @Override
      protected Boolean doInBackground(Void... voids) {
          return false;
      }

      @RequiresApi(api = VERSION_CODES.LOLLIPOP)
      @Override
      protected void onPostExecute(Boolean success) {
          super.onPostExecute(success);
          mService.jobFinished(mParams, !success);
      }
  }

  @RequiresApi(api = VERSION_CODES.LOLLIPOP)
  private static class DownloadService extends JobService {

      private DownloadTask mTask;

      public DownloadService() {
      }

      @Override
      public boolean onStartJob(JobParameters jobParameters) {
          //start task and return true to make system aware and hold the wakelock
          mTask = new DownloadTask(this, jobParameters);
          mTask.execute();

          return true;
      }

      @Override
      public boolean onStopJob(JobParameters jobParameters) {
          //Cancel the task as Job is terminated
          if (mTask != null && !mTask.isCancelled()) {
              mTask.cancel(true);
          }
          //tell system to reschedule
          return true;
      }
  }
}



//Manifest entry
<service  android:name=".DownloadJobSchedular.$DownloadService"
                android:permission="android.permission.BIND_JOB_SERVICE"
                android:exported="true"/>

Next?

To be continued

No comments:

Post a Comment