In Android, a client can bind to a Service
running in a different process and even a different APK, but how could the bound service know the remote client is killed by the system and release the resources?
Death recipients
The Android platform provides a “link to death” facility through the Binder framework to allow a Service
to be notified when any remote client is dead. To get notified, the Service
needs to:
- Obtains a reference to a
Binder
object that lives in the remote client. - Calls the
linkToDeath()
method of the Binder object to register a DeathRecipient callback. - When the remote client is dead, the
binderDied()
method of the callback will be called.
Note that we will only receive death notifications for remote clients, because local clients can only die when the service is also dead.
Show me the code
First, let’s define an AIDL interface to pass the Binder
object to the server:
package com.example.server;
interface IRemoteService {
void register(in IBinder client);
}
Alternatively, we can also use Messenger to pass the Binder
object.
Next, we extend the remote Service
to accept the Binder
object and register the callback:
class RemoteService : Service() {
private val binder = object : IRemoteService.Stub() {
override fun register(client: IBinder?) {
client?.linkToDeath({
// This will be called when the remote client is dead.
// Do the clean-ups you need.
}, 0)
}
}
override fun onBind(intent: Intent?): IBinder = binder
...
}
Now, the client can bind to the service and pass a Binder
object:
class MainActivity : Activity() {
private val binder = Binder()
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
IRemoteService.Stub.asInterface(service).register(binder)
...
}
override fun onServiceDisconnected(name: ComponentName?) {
...
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = Intent().apply {
component = ComponentName("com.example.server", "com.example.server.RemoteService")
}
bindService(intent.setComp, connection, 0)
...
}
...
}
Note that if the client targets Android 11 (API level 30) and above, and tries to connect to a remote service defined in another APK, we need to declare the package visibility needs. Otherwise, it will fail to bind the remote service with bindService()
returning false.
That’s it. Happy hacking!