Become a pro at scheduling tasks in Android

Anshul Jain
AndroidPub
Published in
6 min readOct 24, 2016

Here is a random conversation in the Android universe

Your App: Hey Android dude, what’s up?
Android : Nothing much man, feeling exhausted. Facebook has suck the battery out of me. Why don’t people just uninstall that app?
Your App: Yeah. I have already done that. Hey, I wanted to ask a favour from you.
Android: Yes. Go on..
Your App: I have one task. I want to do it exactly after 2 hours from now.
Android: Sure. We have AlarmManager for this.
AlarmManager : (Grinning)
Your App: Actually, I want to run that task every 2 hours.
Android: Sure man.
AlarmManager : Don’t worry man. I am expert at these kinds of jobs.
Your App: Oh, I forgot one thing. The task should be run only when the network is available.
AlarmManager : (Perplexed). Oh. That is something out of my reach.
Android : I knew this was coming. We have introduced JobScheduler exactly for these types of requirements.
JobScheduler : Yo bro !! (Smiling).
Your App : But JobScheduler was introduced in Lollipop. I have users all the way back to API 14.
JobScheduler - :( :( :(
Android : We always maintain backward compatibility. We have introduced GcmNetworkManager for this purpose. Now schedule your jobs before Lollipop as well.
Your App : Will this also work if the user doesn’t have Google Play Services installed or they do not have the proper version?
GcmNetworkManager : Hey man, what the hell do you want? Most of the devices will have Google Play Services version 7.5 or higher.
Your App : But I can’t take that risk. Even if it means a small number of users.
Firebase Job Dispatcher : Bro, we got you. We will schedule all your tasks. Just tell us your criteria. Rest will be handled by us even if the user does not have Google Play services installed.
Your App : Thanks a lot. :) :) :) (In a relaxed mood).

By now, most of us are familiar with JobScheduler API which was introduced in API 21. Before JobScheduler, we had AlarmManager API which is present from Android version 1. Although significant changes were made in this API from version 19, to save battery of the device and boost the performance, the biggest drawback of Alarm Manager is that it solely works on the basis of time. To overcome this drawback, Google introduced JobScheduler which works on various conditions like availability of network, charging of device.

JobScheduler is good news for all the developers whose app have minimum SDK version set to 21 or greater as they can use this API directly. But what about the apps whose minimum SDK is less than 21?. Thankfully there are a couple of options available to the developers.

GcmNetworkManager

Android bundled this module in Google Play services version 7.5. If your application is running on Lollipop and above, GcmNetworkManager will use the framework’s JobScheduler, so there is almost no difference between GcmNetworkManager and JobScheduler. For those platforms below Lollipop, this class will only work if the Google Play services is installed on the device and is having a version greater or equal to 7.5.

Google has stopped active development on this module and instead recommends to use Firebase Job Dispatcher for scheduling any tasks.

Firebase Job Dispatcher

This library is a wrapper over GcmNetworkManager. But there is one big advantage it holds over GcmNetworkManager. If Google Play Services app is not installed or the app version is less than 7.5, then this library internally uses AlarmManager to schedule the tasks. This can work all the way up to minimum SDK 9. Similar to GcmNetworkManager, this library uses the framework’s JobScheduler if the application is running on Lollipop and above.

Scheduling Jobs using Firebase Job Dispatcher.

In this post, I will be writing about Firebase Job Dispatcher. Google also recommends to use this library. Refer to this page for setting up the library with the latest version.

A job can be defined as the code component which will be run by the Android framework when certain criteria are met. Here is how to create a job.

One thing to consider while creating a job is that whether that job should be persisted on device reboot. If yes, then use LifeTime.FOREVER in setLifeTime() method. But this requires an extra permission RECEIVE_BOOT_COMPLETED

The Job.Builder class provides setConstraints() method to provide the constraints (unmetered network, charging battery etc) required for running a job. Using setTrigger() method, the time window is specified in which the job should be executed. In the above code, the application tells the framework to execute the job anywhere between 30 — 60 seconds from now when any type of Network is available. So the framework will execute the job in that time window only if any kind of network is available. If the constraints are not met, then the framework will keep the job in waiting, and as soon as the required criteria are met, the framework will schedule that job.

This library uses setTag() method to uniquely identify a Job. The tag given to a Job should be unique only on application level and not device level. There is a method setReplaceCurrent() to update the current job based on the tag. If a new Job is created with the same tag and setReplaceCurrent() is not set on the task or is set to false, then the newly created task will be rejected by the Android framework. If setReplaceCurrent() is set to true, then the new task will replace the old task. In the below code I create a new job with different configurations to update the above job.

One you have the Job ready, then you can schedule the job, using schedule() method.

Writing a JobService

When you create a job, you specify a Service to run with setService() argument. There are three methods in this service class which are of most importance.

onStartJob(JobParameters params): When your job is called by the framework, onStartJob() method will be invoked which runs on the main thread. This method returns a boolean which tells the framework whether there is more work remaining. We should consider offloading the code in onStartJob() to a new thread and return true. Returning true here tells the framework that more work is remaining. As soon as the job is completed we can call jobFinished() method which will indicate that the work for this job cycle is completed. If the code involves only some basic operations, then you can complete it in the main thread itself and returns false which means no more work is remaining.

onStopJob(JobParameters params): This method is called when your job is stopped by the framework. The job can be stopped due to various reasons like the running constraints associated with the job are no longer satisfied. The method returns a boolean variable which tells whether the job should be tried again or not. If returned true, then the framework will put up this job again for execution.

jobFinished(JobParameters params, boolean needsReschedule): This method should be called explicitly when a job had been offloaded to a new thread and onStart() method returns true. When the job in the new thread completes, we need to call this method.

In the below piece of code, I call completeJob() method which takes 2 seconds. Therefore I create a new thread and call it from there. At the end I call jobFinished().

Whenever you return true from onStartJob() method, always make sure to inform the framework explicitly that the work for this job has been completed using jobFinished() method . If you fail to do so, the framework will consider the job as still running even if the job is not running.

Cancel a Job

Since every job has a unique tag, a job can be cancelled using cancel() function by passing the tag as the argument. If you need to cancel all the jobs for your application package then call cancelAll() method.


FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
dispatcher.cancelAll(); // Cancel all the jobs for this package
dispatcher.cancel("OneTimeTag"); // Cancel the job for this tag

Thanks for reading the post. You can find my other posts here.

--

--

Anshul Jain
AndroidPub

ex Android Developer | Machine Learning Developer @ Dailyhunt