LiveData and Bound Services

Services have been an oft-overused bit of the Android SDK. Still, a foreground service is a key part of many apps, providing ongoing functionality to the user even though the app’s UI is gone. For example, most audio player apps (music, audiobooks, etc.) rely on foreground services to play that audio.

One way to communicate with a service is to bind to it, using bindService(). This is a very flexible option, as you can define your own API that the service exposes and clients consume. However, binding to services can be tricky, particularly when configuration changes come into the picture.

And any time that configuration changes become a problem, ViewModel and LiveData should be a place to turn to come up with a solution.

So, in this chapter, we will explore how you can wrap your bound service in a Architecture Components-based API.

Old API, New Coat of Paint

Binding to a service sounds easy: call bindService() on a Context, supplying a ServiceConnection object. That ServiceConnection object is called with onServiceConnected() once the binding is ready, and you are passed an IBinder object that you can use to get at the API exposed by the service. Later, you can call unbindService(), passing in the same ServiceConnection, to drop the connection to the service.

However, there are three problems:

  1. It is unsafe to use different Context objects for binding and unbinding
  2. The ServiceConnection object is state — we need to hold onto that to be able to unbind
  3. If the service has no other reason to be around (e.g., something called startService() on it), unbinding from the service immediately destroys it… even if you might bind again milliseconds later

This makes it tricky to bind from an activity, as configuration changes run right into all three of those problems.

The classic solution involved using the Application singleton for binding and a retained fragment for holding onto the ServiceConnection across configuation changes. While the Application singleton is still a good idea, nowadays we can replace the retained fragment with a ViewModel.

Another wrinkle comes with getting data from the service. We can pull such data by calling methods on its API. Or, the service could push data… somehow. For example, we could supply a callback object via the API, which the service can use to provide data updates. However, once again, this callback is part of our state, so we need to think about how we can manage it with our ViewModel. In addition, we have threading to consider, particularly if our service is in a separate process from our UI. This is a place where LiveData can shine.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.