Before Android 8.0, Android apps would start a service that ran almost indefinitely, even when the app was in the background. This service can be handy for the app, and easy for the developer to implement, but can also have a disastrous impact on a user’s device experience.

As an example of this, imagine a scenario where many applications want to perform work when a device is plugged in to charge. When this happens, Android dispatches the android.intent.action.ACTION_POWER_CONNECTED intent. All apps that have registered to respond to that intent will start up, demanding RAM, CPU time, and bandwidth. When this happens, it’s possible these simultaneous demands may exceed the available space on the device, and cause it to slow down. At this point, all a user knows is that they plugged in their device and it started exhibiting sluggish, unresponsive behavior, and may think there is something wrong with their phone or that they need to purchase more memory.

Android 8.0 has introduced new background execution limits that dramatically change how services work. When applications move into the background, any services they start will be granted several minutes to complete their work before the operating system terminates them. The Android Framework JobScheduler is one possible way to address this change for Xamarin.Android apps targeting Android 5.0 (API level 21) or higher.

The Android Framework JobScheduler is an API designed to run jobs—discrete, distinct units of work—in the background of various apps. The JobScheduler schedules which jobs will run at appropriate times according to conditions that can be set by the application. Going back to our original example of charging a device, the JobScheduler may schedule the jobs so that they run one after another, instead of all at once.

The ability to schedule and queue jobs make the JobScheduler perfect for tasks that are traditionally handled by long-running services, such as:

  • Downloading a file, but only when the device is connected to an unmetered (free) network.
  • When charging a device, make a series of thumbnail images from a collection of larger images.
  • If connected to the network, call a method on a web service using an exponential backoff algorithm between unsuccessful web service calls.

There are three important classes in the JobScheduler API:

  • JobScheduler: A system service that will run jobs scheduled by apps.
  • JobService: A class extended by applications and contains the code that runs as part of a job. The code for each job is contained in a class that extends the JobService class and requests the android.permission.BIND_JOB_SERVICE permission.
  • JobInfo: Holds information about the job that the JobScheduler needs in order to run a JobService. The JobInfo will tell the JobScheduler which JobService type to use, and which conditions must be met before the job can run. It also contains any parameters that the app must pass to the JobService. A JobInfo object isn’t directly instantiated, instead it’s created by using a JobInfo.Builder.

A JobService sub-class must override two methods:

  • OnStartJob: The method that a system invokes when the job is starting and runs on the main thread of the app. If the work is a small, easy task (less than 16 milliseconds), it may run on the main thread. However, lengthy tasks such as disk access or network calls must run asynchronously. OnStartJob should return "true" if it’s running work on another thread, while "false" should be returned if the all the work was performed within OnStartJob itself.
  •  OnStopJob: This method is called when the system has to prematurely terminate the job and provides the JobService a chance to perform any necessary cleanup. If the job should be rescheduled, it will return "true".

The following code shows a sample JobService type:

In order to schedule a job, it’s necessary for an app to use a JobInfo.JobBuilder to create a JobInfo object. The JobInfo.JobBuilder has a fluent interface and is used to collect meta-data, such as the type of JobService to instantiate and any conditions that should be met before the job is run. This snippet shows how to create a JobInfo class to run the FibonacciJob (from the example above) but only when the device is connected to an “unmetered” (free) network. The job shouldn’t run sooner than one second from being scheduled but should run within five seconds:

In the previous example, the context is any Android Context, such as an Activity. The parameter is a unique integer that identifies the job service to the JobScheduler.

The following C# extension method should help with creating a ComponentName for a given JobService subclass:

Once a JobInfo created is created, the app can schedule a job. This code snippet will get a reference to the JobScheduler service and ask it to schedule the job identified in the jobInfo object:

It’s also possible for the app to pass parameters to a service by packaging them up in a Bundle and then calling the JobInfo.SetExtras method. The Bundle is included with the JobParameters object passed to the JobService when OnStartJob is invoked. For example, this snippet will pass a value to a job:

To access this value, the JobService should read the .Extras property on the JobParameter object:

Finally, when a JobService has finished its work (regardless of which thread the work is running on), it should call its own JobFinished() method. It’s important to call this method, because it tells the JobScheduler that the work is done and it’s safe to release any wake locks that were acquired for the JobService in the first place. This diagram illustrates how the JobService methods relate to each other and how they would be used:

The relationship of JobService method calls.

Now that you’ve seen the basics of the JobScheduler API, try using it in your Xamarin.Android application to replace a task done in a background service. This is a great opportunity to enhance the experience your user has with your applications and update to Android Oreo 8.0 all at once!

You can find a small sample on GitHub that shows the JobScheduler API in action. The sample calculates a Fibonacci number in a job and then passes that number back to an Activity.

Discuss this post in the forums.