Smart background tasks with JobScheduler

Smart background tasks with JobScheduler

The new release of Android 5.0 provides developers over 5000 new APIs to play with.

One of my favourite component that was relseased with the new SDK is JobScheduler. Briefly:

JobScheduler API that lets you optimize battery life by defining jobs for the system to run asynchronously at a later time or under specified conditions.

Unfortunately, the JobScheduler API is only available for minSdkVersion 21 as it uses system components only available in Android 5.0.

Scheduling a job

In order to schedule a job you must define 2 components:

  • JobInfo that holds the constraints of when the job should run (e.g. only on WiFi, only when plugged in, periodically, etc.)
  • JobService that is a service that handles the scheduled requests.

Lets start by defining our job constraints. Suppose we want to run a background task when the device is connected to WiFi and plugged in. In order to create our JobInfo object we use the JobInfo.Builder :

JobInfo job = new JobInfo.Builder(JOB_ID, new ComponentName(this, SyncJobService.class))  
   .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
   .setRequiresCharging(true)
   .build();

The JobInfo needs an integer id and a service component that will be our implementation of JobService. In order to define our constraints we setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) in order the job to run only on WiFi connection and setRequiresCharging(true) for the device to be plugged in. The only remaining part is to actually schedule the job:

JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);  
jobScheduler.schedule(job);  

Upon calling jobScheduler.schedule(job) the job will be executed if the criteria are met. If you want to make sure that your job will be executed after a period of time regardless of the state of the criteria specified when creating the job, you can use the setOverrideDeadline(long) when building your JobInfo object:

JobInfo job = new JobInfo.Builder(JOB_ID, new ComponentName(this, SyncJobService.class))  
   .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
   .setRequiresCharging(true)
   .setOverrideDeadline(3600000) //the job will be executed anyway after one hour
   .build();

There are a bunch of other parameters to play with when building your job, see them all here.

Creating the JobService

The JobService is the entry point of executing your scheduled job. You create your service by extending JobService and overriding the onStartJob(JobParameters jobParameters) and onStopJob(JobParameters params) callbacks. As their name suggest, the first callback is fired when the job starts - this is the place where the job logic should be implemented - and the second one when the job is stopped by the system because the criteria are not met anymore. You must take your job implementation from the onStartJob callback on another thread as this method is called on the application's main thread.

Lets start by defining our JobService in the AndroidManifest.xml file:

<application ...>  
...
 <service
 android:name="com.catinean.jobschedulerexample.SyncJobService"
 android:permission="android.permission.BIND_JOB_SERVICE"
 android:exported="true" />
...
</application>  

Note that you must protect your service with the android.permission.BIND_JOB_SERVICE permission in order the OS to not ignore it. A simple implementation of a JobService would look like this:

public class SyncJobService extends JobService {

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        new JobTask(this).execute(jobParameters);
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }

    private static class JobTask extends AsyncTask<JobParameters, Void, JobParameters> {
        private final JobService jobService;

        public JobTask(JobService jobService) {
            this.jobService = jobService;
        }

        @Override
        protected JobParameters doInBackground(JobParameters... params) {
            SystemClock.sleep(5000);
            return params[0];
        }

        @Override
        protected void onPostExecute(JobParameters jobParameters) {
            jobService.jobFinished(jobParameters, false);
        }
    }
}

As it can be seen in the above code snippet, when a job is scheduled to start, we execute an AsyncTask that will sleep 5 seconds and then report that the job has finished.

Conclusion

The JobScheduler API is perfect when you want to run background non user-facing tasks when certain criteria are met. The only downside that I see for the moment is that JobScheduler is only available for api level 21 (Android 5.0).

You can find here a full working example based on the snippets above.

comments powered by Disqus