简体   繁体   English

当绑定的客户端断开连接时通知Android服务

[英]Notify an Android service when a bound client disconnects

I have an android service in a remote process that can have multiple bindings from different clients. 我在远程进程中有一个android服务,该服务可以具有来自不同客户端的多个绑定。 My question is, how can the service get notified when a specific bound client gets disconnected unexpectedly (ie the client has crashed)? 我的问题是,当特定绑定的客户端意外断开连接(即客户端崩溃)时,如何通知服务? I can't use onUnbind() , because it only gets called after all clients have been disconnected. 我不能使用onUnbind() ,因为它仅在所有客户端断开连接后才被调用。

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. 这可以通过Binder.linkToDeath()机制来完成-您必须要求每个客户端发送他们发起的new Binder()对象,然后链接到其(您的客户端)死亡。 I'll explain how to preform this using AIDL files. 我将说明如何使用AIDL文件进行此操作。

(You can choose any android IPC mechanism as long as you can pass Binder objects from your client's to your service) (您可以选择任何android IPC机制,只要您可以将Binder对象从客户端传递到服务即可)

Code Example - 代码示例-

Inside your .AIDL file - Create a method to pass the IBinder object from the client to the service 在您的 .AIDL 文件中 -创建一种方法来将IBinder对象从客户端传递到服务

void registerProcessDeath(in IBinder clientDeathListener, String packageName);

On the client side - Initialize a new object and pass it to your service via AIDL interface. 在客户端 -初始化一个新对象,并将其通过AIDL接口传递给您的服务。

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() . 1.获取客户端的Binder并注册到他的linkToDeath()
2. Use helper class to handle all clients via android's IBinder.DeathRecipient class 2.使用助手类通过android的IBinder.DeathRecipient类处理所有客户端

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. 您可以使用拥有的IncomingHandler处理程序,并从客户端发送一条消息,该消息将在调用unbindService(serviceConnection)之前将其取消绑定,保留Messengers(clients)的arraylist并在收到消息时添加/删除。

you can also try to send dummy messages and if you get RemoteException means that the remote client is dead. 您还可以尝试发送虚拟消息,如果收到RemoteException,则意味着远程客户端已死。

check this example http://developer.android.com/reference/android/app/Service.html 检查此示例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);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM