To avoid running into any issues with these changes now in place, there are a number of different approaches that we can take. These include:

Scheduling jobs

So to begin with, using a Job Scheduler allows us to move away from our background service implementation and let the system take care of the execution for us. The system will also be intelligent with the jobs that we schedule, so scheduling multiple jobs means that they can be grouped and executed at the same time. We can also schedule jobs to run based on certain criteria, such as network connectivity and device power status — so not only does adapting to these changes avoid issues with the way things are now, but it also allows our application tasks to be run more efficiently.

You can check out the documentation for the Android Job Scheduler here. There is also the Firebase Job Dispatcher which allows you to achieve similar results when targeting API versions lower than Android 5.0.

Firebase Cloud Messaging and Service Whitelist

When it comes to services running in the background, there is a whitelist which is used to allow applications to temporarily run in the background as if they were running in the foreground. Applications will be included on this whitelist in situations when they are:

Being triggered from a high priority FCM/GCM notification

Performing SMS/MMS delivery

Acting from a Notification Action

So for example, in cases where we may need our application data to be refreshed based off of changes on the server, we can use FCM to send our application a notification that can trigger a service to fetch the latest set of data. For this, using High Priority messages allows our application to be added to a service whitelist which allows our services to run as if they were in the foreground. High Priority messages from FCM will be received immediately, even when the system is in the Doze state —and this is the point that the application will also be added to the temporary service whitelist. This means that we can start our service to update our application’s data as if our application was running in the foreground.

It’s important to note that this will only be the case for High Priority messages, other priority messages will be received when the device screen is turned back on or during the Doze maintenance window.

Starting a service in the foreground

In some cases, we may be running a service which is carrying out a task that the user may need to interact with or monitor the task that is being executed. Some examples of this could be when the user is downloading some new content in your app, using a timer to perform some time based operation, or maybe they’re receiving navigational directions from your application — these are all situations where the user needs to be aware of the task at hand.

For these kind of situations, we can run our service in the foreground — this is because when running, foreground services use a persistent notification to make the user aware that they are currently running.

Previously, we were able to start foreground services by simply calling startForeground() directly — using this method directly no longer works, so we need to make use of the new startForegroundService() method. The method itself is the same as calling startService(), but with the contract that startForeground() will be called. The difference with using this method as compared to startService() is that we can call it at anytime, even if our application is not currently in the foreground.

We can implement this by:

First using the startForegroundService() method, passing an intent for our service task to be carried out. This method will create our background service that we must immediately promote to the foreground.

Within this service we need to create a notification to be displayed for our foreground service. This must be of at least Low priority or higher so that is shown to the user on screen — if the priority is set to PRIORITY_MIN then the notification will not be displayed to the user.

Low priority or higher so that is shown to the user on screen — if the priority is set to then the notification will not be displayed to the user. Next, we can call startForeground(id, notification) from within the service — this will promote our service to the foreground.

Defer task to the foreground

If none of the above are appropriate solutions for your application, then another approach that can be taken is to simply defer the task to the foreground of the application. Personally I’d advise exploring the above solutions first as they will help you to avoid blocking your user interface and create a smoother experience for your user.