I have an android service in a remote process that can have multiple bindings from different clients. My question is, how can the service get notified when a specific bound client gets disconnected unexpectedly (i.e the client has crashed)?
I can't use onUnbind()
, because it only gets called after all clients have been disconnected.
public class MyService extends Service {
final Messenger mServiceMessenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
return mServiceMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// Handling messages logic...
}
}
}
This can be done by the Binder.linkToDeath()
mechanism - You'll have to ask each client to send new Binder()
object that they initiated and then link to their (your clients') death.
I'll explain how to preform this using AIDL files.
(You can choose any android IPC mechanism as long as you can pass Binder objects from your client's to your service)
Code Example -
Inside your .AIDL
file - Create a method to pass the IBinder
object from the client to the service
void registerProcessDeath(in IBinder clientDeathListener, String packageName);
On the client side - Initialize a new object and pass it to your service via AIDL interface.
public void onServiceConnected(ComponentName className, IBinder service) {
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
//Call the registerProcessDeath method from the AIDL file and pass
//the new Binder object and the client's package name
mIMyAidlInterface.registerProcessDeath(new Binder(),getPackageName());
}
On the service side -
1. Get the client's Binder
and register to his linkToDeath()
.
2. Use helper class to handle all clients via android's IBinder.DeathRecipient class
public class MyService extends Service {
//Helper class to handle all client's deaths.
private volatile ClientsDeathWatcher mClientsList;
@Override
public IBinder onBind(Intent intent) {
mClientsList = new ClientsDeathWatcher();
return mStub;
}
private final IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
@Override
public void registerProcessDeath(IBinder cb, String packageName){
boolean isRegistered = mClientsList.register(cb , packageName);
}
};
}
//This is thread-safe helper class to handle all
//the client death related tasks.
//All you care abut is the clientDeath() method.
public class ClientsDeathWatcher {
private ArrayMap<String, DeathCallBack> mCallbacks = new ArrayMap<>();
private final class DeathCallBack implements IBinder.DeathRecipient {
private String pn;
private IBinder mBinder;
DeathCallBack(String packageName,IBinder binder) {
pn = packageName;
mBinder = binder;
}
public void binderDied() {
synchronized (mCallbacks) {
mBinder.unlinkToDeath(this,0);
clientDeath(pn);
}
}
}
//To be called only from thread-safe functions
private void clientDeath(String packageName) {
mCallbacks.remove(packageName);
//Do your stuff here.
//$$$$$$$$$
}
public boolean register(IBinder token, String packageName) {
synchronized (mCallbacks) {
try {
if (!mCallbacks.containsKey(packageName)) {
DeathCallBack mDeathCallBack = new DeathCallBack(packageName,token);
mCallbacks.put(packageName, mDeathCallBack);
//This is where the magic happens
token.linkToDeath(mDeathCallBack, 0);
}
return true;
} catch (RemoteException e) {
e.printStackTrace();
return false;
}
}
}
}
you can use the IncomingHandler handler you have and send a message from the client that it will be unbinded before calling unbindService(serviceConnection), keeping arraylist of the Messengers(clients) and add/remove when a message is received.
you can also try to send dummy messages and if you get RemoteException means that the remote client is dead.
check this example http://developer.android.com/reference/android/app/Service.html
extract:
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_VALUE:
mValue = msg.arg1;
for (int i=mClients.size()-1; i>=0; i--) {
try {
mClients.get(i).send(Message.obtain(null,
MSG_SET_VALUE, mValue, 0));
} catch (RemoteException e) {
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
break;
default:
super.handleMessage(msg);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With