21 February 2015 |

One of the really cool things about Lollipop is the new JobScheduler API. Not only is this API exciting for developers but end users should also be excited. This surprisingly easy to use API lets your app schedule a job to take place according to a number of parameters. The cool thing about this is that you can easily set parameters that will save the end user lots of battery life, giving your users one less reason to uninstall your app. There are three main parts to this API: JobInfo, JobService and JobScheduler.

JobInfo and Available Parameters

Parameters are defined by the JobInfo class and constructed using the JobInfo.Builder. The following are the different parameters that you can set.

If we were the PlayStore we would probably want to update apps when:

  • on wifi to save the user data.
  • the device is idle so that we don’t slow down the user’s device experience. No more updating when the screen turns on!
  • the device is charging, allowing us to save the user battery life during the day.
ComponentName serviceName = new ComponentName(context, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, serviceName)
        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
        .setRequiresDeviceIdle(true)
        .setRequiresCharging(true)
        .build();

What’s MyJobService.class? Don’t freak out, we’re getting to that now.

JobService

The JobService is the actual service that is going to run your job. This service has different methods to implement than normal services. The first method is onStartJob(JobParameters params). This method is what gets called when the JobScheduler decides to run your job based on its parameters. You can get the jobId from the JobParameters and you will have to hold on to these parameters to finish the job later. The next method is onStopJob(JobParameters params). This will get called when your parameters are no longer being met. In our previous example this would happen when the user switches off of wifi, unplugs or turns the screen on their device. This means that we want to stop our job as soon as we can; in this case we wouldn’t update any more apps.

There are three very important things to know when using a JobService.

  • The JobService runs on the main thread. It is your responsibility to move your work off thread. If the user tries to open your app while a job is running on the main thread they might get an Android Not Responding error (ANR).
  • You must finish your job when it is complete. The JobScheduler keeps a wake lock for your job. If you don’t call jobFinished(JobParameters params, boolean needsReschedule) with the JobParameters from onStartJob(JobParameters params) the JobScheduler will keep a wake lock for your app and burn the devices battery. Even worse is that the battery history will blame your app. Here at Two Toasters we call that type of bug a 1 star, uninstall.
  • You have to register your job service in the AndroidManifest. If you do not, the system will not be able to find your service as a component and it will not start your jobs. You’ll never even know as this does not produce an error.
<application
    .... stuff ....
    >

    <service
        android:name=".MyJobService"
        android:permission="android.permission.BIND_JOB_SERVICE"
        android:exported="true"/>
</application>
public class MyJobService extends JobService {

    private UpdateAppsAsyncTask updateTask = new UpdateAppsAsyncTask();

    @Override
    public boolean onStartJob(JobParameters params) {
        // Note: this is preformed on the main thread.

        updateTask.execute(params);

        return true;
    }

    // Stopping jobs if our job requires change.

    @Override
    public boolean onStopJob(JobParameters params) {
        // Note: return true to reschedule this job.

        boolean shouldReschedule = updateTask.stopJob(params);

        return shouldReschedule;
    }

    private class UpdateAppsAsyncTask extends AsyncTask<JobParameters, Void, JobParameters[]> {


        @Override
        protected JobParameters[] doInBackground(JobParameters... params) {

          // Do updating and stopping logical here.
          return params;
        }

        @Override
        protected void onPostExecute(JobParameters[] result) {
            for (JobParameters params : result) {
                if (!hasJobBeenStopped(params)) {
                    jobFinished(params, false);
                }
            }
        }

        private boolean hasJobBeenStopped(JobParameters params) {
            // Logic for checking stop.
        }

        public boolean stopJob(JobParameters params) {
            // Logic for stopping a job. return true if job should be rescheduled.
        }

    }
}

Of course this is just stubbed out and you can handle your off thread logic however you so wish. Just remember that however you handle threading you have to call jobFinished(JobParameters params, boolean needsReschedule) when you’re done.

JobScheduler, wait… its how easy?

This part is really darn easy. We now have our JobInfo and our JobService so it is time to schedule our job! All we have to do is get the JobService the same way you would get any system service and hand it our JobInfo with the schedule(JobInfo job) method. Noob tip: your activity is a context object.

JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = scheduler.schedule(jobInfo);
if (result == JobScheduler.RESULT_SUCCESS) Log.d(TAG, "Job scheduled successfully!");

Conclusion

Not too bad eh? Way easier than setting up a SyncAdapter plus it’s more powerful and flexible than the AlarmManager! If you want to check out the code in action, head to our Github repo and build it yourself! Thanks for the read and as always, keep on toasting.


Categories

Tags