Project Volta is the name given to features of Android 5 aimed at improving power usage on Android devices. Volta includes better tools for monitoring battery usage.  I’ll be covering those tools in an upcoming post.  It also includes a new API for scheduling work – a job scheduler – which I cover in this Android Development Tutorial.

You might ask why is a job scheduler API part of a power improvement effort?  The idea is that often times, the battery life of our devices is better served when more taxing tasks are performed under certain conditions – like when the device is plugged in or connected via WIFI versus using the mobile carrier network.  Of course, the job scheduling API can also be used to schedule asynchronous work that isn’t always about battery improvement.  Sometimes there are tasks that just need to occur regularly or when the device is otherwise unoccupied.

Now scheduling work in Android is not new.  The AlarmManager in Android, for example, has been a means to repetitively schedule an application or work to kick off since Android API 1.  The new job scheduling API differs from the AlarmManager in that it is more aware of the environmental resources and can kick of batch jobs when certain resources are more available.

Job Scheduling API

There are several new classes in Android 5 to help create and execute your scheduled tasks.

JobScheduler Service

There is a service for most things in Android.  A new service, the JobScheduler Service, helps you manage scheduled tasks.  Obtain the JobScheduler service via getSystemServices( ).

JobScheduler jobScheduler = (JobScheduler) getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);

Call getSystemServices( ) on a Context – the global application context is used in the example above.

The scheduler has methods to schedule, cancel and see the list of pending jobs.  Jobs are represented by JobInfo objects.

Job Scheduling API

You now need to build a job and schedule it for execution.  Again a familiar pattern is used in Android to help you create a scheduled job  – the builder.  Use a Builder to create a new JobInfo object.

ComponentName componentName = new ComponentName(getApplicationContext(),TestService.class);
JobInfo jobInfo =  new JobInfo.Builder(1,componentName).setMinimumLatency(1000).build();

The Builder requires the JobService or “job” name (the name is TestService in the example code above) and the Job ID.  The Job ID (1 in the example above) can be used to cancel the job via the JobScheduler.

Note the call to setMinimumLatency( ) before the call to build the JobInfo object?  This is the first of many optional method calls used when creating the JobInfo.

The JobInfo object encapsulates all the information about the work you want accomplished and the conditions of its execution (time, power availability, etc.).  JobInfo isn’t the actual work or task itself.  The actual work to be accomplished is defined in a JobService (like TestService above).  The JobInfo just represents the parameters or criteria about when you want the work to execute.  It is in the JobInfo object, for example, that you specify that a big downloading job is to run when the device is plugged in and has a WIFI connection.

Optional parameters or criteria to the JobInfo include:

  • make the job periodic (a repeating job within a given period)
  • the interval between periodic job runs
  • the maximum execution delay – how long to wait to kick of the job (not a parameter for a periodic job task)
  • the minimum amount of time delay before starting the job (not a parameter for a periodic job task)
  • the device must be plugged in and charging in order to run the job
  • the device must be idle (“in an idle maintenance window”) in order to run the job
  • the device must have a network connection (either WIFI, unmetered, or any)
  • the device must not be connected to a network
  • the job should be rescheduled if the device reboots (called a “persistent” job).

* all time related parameters are specified in milliseconds.

Lastly, you can also set what is called a back off policy with the JobInfo.  The backoff policy applies to tasks that have finished and need to be retried (perhaps because of a failure or the need to do the task again).  The backoff policy allows the job to be retried, but under additional parameters like a delay before rescheduling the job or even growing the delay in an exponential way for repeated failures.  Consider the situation whereby your backend service goes down for some extended period.  During that time, the mobile application running on multiple devices may all want to keep retrying until the service comes back up.  The idea behind the backoff policy is that is allows you to determine how to stagger and/or ease retries so that once the backend server comes back on line, there is not a glut of request from the mobile clients – possibly causing it to go down again.

Each of the optional parameters are set with a method call to the builder.  Here, for example, is how to specify that you want your job (again, defined by TaskService) to kick off only when there is any network connection, when the device is plugged in, and repeats at least every 12 hours (43200000 milliseconds).

ComponentName componentName = new ComponentName(getApplicationContext(), TestService.class);
JobInfo jobInfo = new JobInfo.Builder(1, componentName).setPeriodic(43200000)
  .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).setRequiresCharging(true).build();
Creating the Service

Where is the work of the job specified?  The object that contains the job or work to be accomplished is an instance of JobService.  JobService (android.app.job) is a subclass of the the good-old Android Service class.  As such, it represents background (non-UI) work that is to accomplished.  It is important to note that by default, just as with regular Android Service objects, the JobService runs on the main / UI thread.  As with a regular Service, you must take care of getting the execution of the task to a non-UI thread – such as creating and using an AsyncTask.  (see my earlier posts about thread options and how to deal with other threads and communication with the UI thread).

Unlike the super class Service, you do not override the onStartCommand( ) method in a JobService.  Instead, the JobService has new methods:  onStartJob( ) and onStopJob( ).  The onStartJob( ) method gets called when the JobScheduler runs your job per the conditions specified in the parameters of your JobInfo.  The onStartJob( ) method runs on the main / UI thread.  It is typically in this method that you create and kick off another thread to take care of the work of your job.  The onStartJob( ) method returns a Boolean true if the service needs to process the work and false if there is no more work to be done for the job.

The onStopJob( ) method gets called by the system when your job is executing and it detects that the criteria or parameters associated with your job via the JobInfo no longer apply and it must stop your job.  For example, if you specified in your JobInfo that the job must have network access to run, the onStopJob( ) will get called when network access is no longer available.  This method also executes on the main / UI thread.  The onStopJob( ) method also returns a Boolean to indicate whether you would like the job to rescheduled / retried when conditions are right again.

One other very important method built in to the JobService superclass is a method that you cannot override.  The final jobFinished( ) method must be called by your code when your job is done executing.  This notifies the system’s “JobManager” that it is finished so that it no longer needs to be managed, rescheduled, etc.  One of the parameters (a Boolean true) to jobFinished( ) allows you to indicate that you want the job to be rescheduled according to the back-off criteria.

Here is a complete example of the example JobService –  TestService – with its onStartJob( ) and onStopJob( ) methods along with an inner AsyncTask class used to demonstrate how to possibly move the actual work to a separate thread.

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.AsyncTask;
import android.util.Log;

public class TestService extends JobService {

    JobParameters params;
    DoItTask doIt;

    @Override
    public boolean onStartJob(JobParameters params) {
        this.params = params;
        Log.d("TestService", "Work to be called from here");
        doIt = new DoItTask();
        doIt.execute();
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d("TestService", "System calling to stop the job here");
        if (doIt != null)
            doIt.cancel(true);
        return false;
    }

    private class DoItTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected void onPostExecute(Void aVoid) {
            Log.d("DoItTask", "Clean up the task here and call jobFinished...");
            jobFinished(params, false);
            super.onPostExecute(aVoid);
        }

        @Override
        protected Void doInBackground(Void... params) {
            Log.d("DoItTask", "Working here...");
            return null;
        }
    }

}

Now, all that is left is to show you how to start the JobService, given the JobInfo and JobScheduler.  Note the last line in the code below.

JobScheduler jobScheduler = (JobScheduler) getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);
ComponentName componentName = new ComponentName(getApplicationContext(), TestService.class);
JobInfo jobInfo = new JobInfo.Builder(1, componentName).setOverrideDeadline(10).setRequiresCharging(true).build();
jobScheduler.schedule(jobInfo);
Wrap Up

App power utilization is serious business. At last year’s AnDevCon conference, several speakers indicated that one of the best ways to get yourself a low rating on Google Play is to consume too much power. Users don’t like their phones and tablets going dead because of your poor decision making. So use the JobScheduler to schedule power-expensive tasks when it is unlikely to affect the device’s battery life. Tune in for my next post where we’ll take a look at more elements of Project Volta in Android 5. If you found this post helpful, let us know by leaving a comment or clicking the Recommmend button at the bottom of the page. If you need help on your Android project or even learning Android, I hope you will consider Intertech to provide assistance.