Android Services: Getting Started
Learn about Android Services and the differences between foreground, background and bound services. By Gabriela Kordić.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android Services: Getting Started
30 mins
- Getting Started
- Introducing Android Services
- Declaring a Service in the Manifest
- Implementing the Service Interface
- Creating a Foreground Service
- Implementing the Service Methods
- Adding Permissions
- Creating a Notification Channel
- Creating a Notification Builder
- Building a Notification
- Starting and Stopping the Service
- Updating the Notification When the Timer Changes
- Using Background Processing for Complex Work
- Explaining Background Execution Limits
- Creating a Bound Service
- Converting MusicService into actual Service
- Defining a Binder
- Defining Service Methods for Managing Audio
- Creating a Service Connection Callback
- Binding to the Service
- Using Service Methods
- Interprocess Communication
- Where to Go From Here?
Creating a Service Connection Callback
To use MusicService
inside other classes, you have to create its instance. Open MainActivity.kt and add the following at the top of the class:
private var musicService: MusicService? = null
Here, you’re declaring a service variable that holds a service instance. For now, the service is null. You’ll assign a new value when the activity connects to the service by using the Binder interface. To catch the connection state changes, create a connection callback.
Add the code below the musicService
initialization:
// 1
private val boundServiceConnection = object : ServiceConnection {
// 2
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder: MusicService.MusicBinder = service as MusicService.MusicBinder
musicService = binder.getService()
mainViewModel.isMusicServiceBound = true
}
// 3
override fun onServiceDisconnected(arg0: ComponentName) {
musicService?.runAction(MusicState.STOP)
musicService = null
mainViewModel.isMusicServiceBound = false
}
}
That code is easy to digest:
- This is a callback for the service connection state.
- When activity connects to service, the system uses
MusicBinder
instance andgetService()
to give a reference tomusicService
. - When service disconnects, the audio will stop if the service reference isn’t already
null
, clearing the service reference.
This sets a flag, isMusicServiceBound
, that checks a service-bound state in both methods according to the parameter you provided. This is important to avoid DeadObjectException exceptions, which remote methods throw when the connection breaks.
Binding to the Service
Your next step is to bind the service when MainActivity
starts. Find onStart()
and add:
if (!mainViewModel.isMusicServiceBound) bindToMusicService()
Here, you’re checking whether the service has a binding already. If not, you call a binding method.
The binding method doesn’t exist yet, so add its code below onDestroy()
:
private fun bindToMusicService() {
// 1
Intent(this, MusicService::class.java).also {
// 2
bindService(it, boundServiceConnection, Context.BIND_AUTO_CREATE)
}
}
In this code, you:
- Declare an intent to start
MusicService
. - Provide the
Intent
to the service along with the connection callback and a flag that automatically creates the service if the binding exists.
To avoid memory leaks or bugs, you need to add code to unbind the service. Add the following to unbindMusicService()
:
unbindService(boundServiceConnection)
With this, you tell the service to execute onServiceDisconnected()
in the boundServiceConnection
callback.
Using Service Methods
Once the service unbindes, you need to stop the music. Add the following code above the unbindService()
line you just added:
musicService?.runAction(MusicState.STOP)
Here, you use the service instance to stop the audio. Remember that you weren’t able to call methods from TimerService
. Then, you needed to provide flags through Intent
. However, here you have a connection — a binding — to the service, so you’re able to call its methods and receive a response.
To trigger an audio action, use sendCommandToBoundService()
. Now, create a generic call for changing the action to reduce a number of code lines. Add this line to sendCommandToBoundService()
:
musicService?.runAction(state)
With this, you tell the service to execute an action that matches the sendCommandToBoundService()
parameter.
There is one more thing to do! Once the song starts to play, the service can provide info about the song’s name. Before you try that out, you need to fix the part with receiving a result.
Inside getNameOfSong()
, replace the line which returns “Unknown” text with:
musicService?.getNameOfSong() ?: getString(R.string.unknown)
Here, you call a method from the service that checks which audio track is currently playing and returns an optional String
result. If the result is null
, you use a resource text instead.
Build and run, then press the Play icon to start the audio. Press GET SONG NAME to see the running song name in a Toast message. Finally, you can enjoy sound while playing your game!
Interprocess Communication
Every Android app runs in its own process with a unique process ID. When you start an app, the Android system generates a process and runs a main activity on the main thread. This has the advantage that the app lives in an isolated environment and other apps can’t interrupt it.
Based on this, Android allows you to run components in a different process that isn’t used to start an app. To do this, you need to use the process tag inside AndroidManifest.xml. The process can have a random name, like myNewProcess.
Multiprocessing gives your app better security and memory management. If you use it, however, you need to find a way to communicate between different processes.
You only need to include one programming interface — IBinder — but Android provides three ways of defining it:
You already implemented the first approach in this tutorial, but feel free to investigate the other two.
Where to Go From Here?
Download the final project using the Download Materials button at the top or bottom of this tutorial.
You’ve learned a lot of new things about Android Services and how to implement them in different ways. Services are useful for app optimization and improving the user experience.
Here are some links you can refer to:
Android 12 will bring some changes to foreground notifications. Check them out in the official foreground services documentation.
If you have any questions or comments, feel free to join the forum discussion below.