首先我们来还原这个问题:
首先,我创建了NewMessage类用来表示进程间传递的消息,实现了Parcelable序列化接口:
- public class NewMessage implements Parcelable {
-
- public String senderID;
- public String messageContent;
- public String receiverID;
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(senderID);
- out.writeString(messageContent);
- out.writeString(receiverID);
- }
-
- public static final Parcelable.Creator<NewMessage> CREATOR = new Creator<NewMessage>() {
-
- @Override
- public NewMessage[] newArray(int size) {
- return new NewMessage[size];
- }
-
- @Override
- public NewMessage createFromParcel(Parcel in) {
- return new NewMessage(in);
- }
- };
-
- private NewMessage(Parcel in)
- {
- senderID = in.readString();
- messageContent = in.readString();
- receiverID = in.readString();
- }
-
- public NewMessage(String senderID,String messageContent,String receiverID)
- {
- this.senderID = senderID;
- this.messageContent = messageContent;
- this.receiverID = receiverID;
- }
- }
接着定义了消息本身的aidl接口
- package com.hzw.messagesend;
-
- parcelable NewMessage;
定义了用于向服务端注册和解注册的接口:
- package com.hzw.messagesend;
-
- import com.hzw.messagesend.NewMessage;
- import com.hzw.messagesend.ILoginOnListener;
- interface IMessageManager
- {
- void registerLoginUser(ILoginOnListener listener);
- void unRegisterLoginUser(ILoginOnListener listener);
- }
因为在IMessageManager中用到了ILoginOnListener,这个接口里面有注册的方法,所以我们还需要定义ILoginOnListener的aidl接口,原因在于AIDL中是无法使用普通接口的,这个ILoginOnListener存在的目的是:我们的服务端要回传消息给客户端,此时原先的服务端将成为客户端,原先的客户端将成为服务端,两者之间同样是跨进程通信,那么我们就必须让ILoginOnListener成为能在Binder中传输的对象了,也就只好将其也定义成aidl接口了;
ILoginOnListener.aidl
- package com.hzw.messagesend;
-
- import com.hzw.messagesend.NewMessage;
-
- interface ILoginOnListener
- {
- void OnNewMessageArrived(in NewMessage newMessage);
- }
服务端:
- public class MessageSendService extends Service{
-
-
- public AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
-
- public CopyOnWriteArrayList<ILoginOnListener> list = new CopyOnWriteArrayList<ILoginOnListener>();
-
- public IBinder binder = new IMessageManager.Stub() {
-
- @Override
- public void unRegisterLoginUser(ILoginOnListener listener)
- throws RemoteException {
- if(list.contains(listener))
- list.remove(listener);
- else
- System.out.println("没找到用户,解除绑定失败......");
- }
-
- @Override
- public void registerLoginUser(ILoginOnListener listener)
- throws RemoteException {
- if(!list.contains(listener))
- list.add(listener);
- else
- System.out.println("此用户已经正在监听......");
- }
- };
-
-
- @Override
- public void onCreate() {
- super.onCreate();
- new Thread(new MessageRunnable()).start();
- }
- @Override
- public void onDestroy() {
- mIsServiceDestroyed.set(true);
- super.onDestroy();
- }
-
- private class MessageRunnable implements Runnable{
- public void run() {
- while(!mIsServiceDestroyed.get())
- {
-
- int senderID = (int)(Math.random()*10);
- int receiverID = (int)(Math.random()*2);
- if(senderID == receiverID)
- continue;
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- String messageContent = getNowTime()+"** "+senderID+"向"+receiverID+"发送了一条消息";
-
- NewMessage message = new NewMessage(senderID+"", messageContent, receiverID+"");
- onNewMesssageArrived(message);
- System.out.println(messageContent);
- }
- }
- };
-
-
- public void onNewMesssageArrived(NewMessage message)
- {
- for(int i = 0;i < list.size();i++)
- {
- ILoginOnListener listener = list.get(i);
- try {
- if(listener != null)
- listener.OnNewMessageArrived(message);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
-
- public String getNowTime()
- {
-
- return new SimpleDateFormat("(HH:mm:ss)").format(new Date(System.currentTimeMillis()));
- }
- @Override
- public IBinder onBind(Intent arg0) {
- return binder;
- }
- }
服务端代码也比较简单,这里模拟了有10个用户每隔500ms给两个用户来发送消息,同时在注册和解注册方法中分别打印了Log;
客户端:
- public class MessageSendActivity extends Activity implements OnClickListener{
-
- public TextView mTextView;
- public Button mStartReceive;
- public Button mStopReceive;
- public static final int NEW_MESSAGE = 1;
- public IMessageManager mManager;
- public boolean isRegister = false;
- public boolean isRepeat = false;
-
- public Handler mHandler = new Handler(){
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case NEW_MESSAGE:
- NewMessage message = (NewMessage) msg.obj;
- mTextView.setText(mTextView.getText()+"\n"+message.messageContent);
- break;
- default:
- break;
- }
- };
- };
-
- public ILoginOnListener listener = new ILoginOnListener.Stub(){
- @Override
- public void OnNewMessageArrived(NewMessage newMessage)
- throws RemoteException {
- if(newMessage.receiverID.equals("1"))
- {
-
- mHandler.obtainMessage(NEW_MESSAGE,newMessage).sendToTarget();
- }
- }
- };
-
- public DeathRecipient mDeathRecipient = new DeathRecipient() {
-
- @Override
- public void binderDied() {
- System.out.println("binderDied: "+Thread.currentThread().getName());
- if(mManager == null)
- return;
-
- mManager.asBinder().unlinkToDeath(mDeathRecipient,0);
- mManager = null;
-
- Intent intent = new Intent(MessageSendActivity.this, MessageSendService.class);
- bindService(intent, connection, Context.BIND_AUTO_CREATE);
- isRepeat = true;
- }
- };
-
- public ServiceConnection connection = new ServiceConnection() {
-
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
- mManager = null;
- }
-
- @Override
- public void onServiceConnected(ComponentName arg0, IBinder service) {
- IMessageManager manager = IMessageManager.Stub.asInterface(service);
- try {
- service.linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e1) {
- e1.printStackTrace();
- }
- mManager = manager;
- if(isRepeat == true)
- System.out.println("Binder死亡之后重新建立的连接");
- else
- System.out.println("Binder没有死亡");
- isRepeat = false;
- try {
- manager.registerLoginUser(listener);
- isRegister = true;
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- initView();
- }
-
- public void initView()
- {
- mTextView = (TextView)findViewById(R.id.textView);
- mStartReceive = (Button)findViewById(R.id.startReceive);
- mStopReceive = (Button)findViewById(R.id.stopReceive);
- mStartReceive.setOnClickListener(this);
- mStopReceive.setOnClickListener(this);
- }
-
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.startReceive:
-
- Intent intent = new Intent(this, MessageSendService.class);
- bindService(intent, connection, Context.BIND_AUTO_CREATE);
- break;
- case R.id.stopReceive:
- if(isRegister)
- {
-
- if(mManager != null && mManager.asBinder().isBinderAlive())
- {
- try {
- mManager.unRegisterLoginUser(listener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- isRegister = false;
- }
- break;
- default:
- break;
- }
- }
-
- @Override
- protected void onDestroy() {
- if(isRegister)
- {
-
- if(mManager != null && mManager.asBinder().isBinderAlive())
- {
- try {
- mManager.unRegisterLoginUser(listener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
-
- unbindService(connection);
- super.onDestroy();
- }
- }
客户端模拟了当前用户ID是1,并且在收到消息之后判断接受ID是不是1,如果是的话会通过Handler发送到主线程上面,同时更新界面显示,这里为什么要用到Handler,原因在于OnNewMessageArrived方法是运行在客户端的Binder线程池中的,并不是主线程,不能进行更新UI的操作,为了防止服务端进程被杀死,我们在客户端设置了死亡代理,只要服务端进程被意外关掉,都会回调
binderDied方法,在这个方法里面我们做了重新绑定service操作,其他部分代码就不再详解了;
查看下输出效果:
我们在点开始接收消息的时候,每隔500ms就有别的客户端向2个客户端随机发消息,上面显示的是客户端1收到的消息过程;
但是当我们点击停止接收消息后,查看Log输出:
可以看到在我们点击完按钮之后,消息并没有停止接收,而是输出解除绑定失败,这是怎么回事呢?
原因就在于我们的注册对象listener是在进程间传输的,Binder在服务端会把客户端传递过来的对象重新转换为新的对象,因而注册和解注册的根本就不是一个对象,当然不能达到解除注册的目的了;
我们在服务端代码第12行之后添加下面两行代码:
- System.out.println("解绑定时候的listener: "+listener);
- System.out.println("解绑定时候的listener对应的Binder: "+listener.asBinder());
在服务端21行之后添加下面两行代码:
- System.out.println("绑定时候的listener: "+listener);
- System.out.println("绑定时候的listener对应的Binder: "+listener.asBinder());
而后运行,查看Log输出:
从Log输出上面很明显的可以看到绑定和解绑定的listener并不是同一个对象,那么解除绑定肯定是失败的,但是从Log输出上可以看出listener所对应的Binder对象是相同的,他们都是BinderProxy对象,原因在于:跨进程间通信时,服务端要创建Binder对象,客户端要拿到服务端Binder对象在Binder驱动中的引用,对应到客户端就是BinderProxy对象了,我们这里的情况是服务端和客户端通信,那么此时的服务端将成为客户端,所以收到的Binder就是BinderProxy类型了,为什么绑定和解绑定过程中listener对象不同,但是listener对象对应的Binder对象却的相同的呢?这个我们需要看下系统为我们的IMessageManager.aidl生成的.Java文件内容了:
在他内部的代理类中,我们看到如下代码:
- @Override
- public void registerLoginUser(com.hzw.messagesend.ILoginOnListener listener) throws android.os.RemoteException
- {
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
- mRemote.transact(Stub.TRANSACTION_registerLoginUser, _data, _reply, 0);
- _reply.readException();
- }
- finally {
- _reply.recycle();
- _data.recycle();
- }
- }
在第8行,你会看到调用了writeStrongBinder方法,这个方法会将listener对应的Binder对象写进去,那么我们在调用服务端的onTransact方法的时候就会获取到这个Binder对象,我们可以来看看服务端的onTransact方法case语句为TRANSACTION_registerLoginUser的部分:
- case TRANSACTION_registerLoginUser:
- {
- data.enforceInterface(DESCRIPTOR);
- com.hzw.messagesend.ILoginOnListener _arg0;
- _arg0 = com.hzw.messagesend.ILoginOnListener.Stub.asInterface(data.readStrongBinder());
- this.registerLoginUser(_arg0);
- reply.writeNoException();
- return true;
- }
可以看到第5行调用了readStrongBinder方法,从输入数据中取出了Binder对象,接下来在调用unRegisterLoginUser的时候也会调用writeStrongBinder写入listener对应的Binder对象,调用readStrongBinder读出写入的Binder对象,而因为是进程间的通信,所以这里的Binder对象实际上是BinderProxy对象,对于同一进程而言,其获取到的BinderProxy对象是相同的,所以registerLoginUser和
unRegisterLoginUser写入的Binder是同一个Binder;
我们可以利用registerLoginUser和unRegisterLoginUser写入的Binder一致来解决无法解除绑定注册这个问题:
将服务端unRegisterLoginUser修改成下面这样:
- public void unRegisterLoginUser(ILoginOnListener listener)
- throws RemoteException {
- for(int i = 0;i < list.size();i++)
- {
- if(list.get(i).asBinder() == listener.asBinder())
- {
-
- System.out.println("解绑定时候的listener: "+listener);
- System.out.println("解绑定时候的listener对应的Binder: "+listener.asBinder());
- list.remove(list.get(i));
- break;
- }
- }
- }
随后运行程序,点击停止消息接收,可以发现界面上不再有消息刷新出现;
上面解除绑定注册的方法比较暴力,就是直接循环看看我的所有注册的listener里面有没有listener对应的Binder等于要解除的listener的,有的话,直接将该listener从监听列表中删除就可以了;
可以发现,上面的暴力式方式完全可以用Maap来实现,原因就在于每一个进程对应的Binder对象是唯一的,只要是在同一个进程中,不管你在哪获取Binder都是相同的,那么我们可以把Binder作为Map的key值,value值就可以是listener对象了,因为每一个进程对应一个用户嘛,这样相对来说效率比较高,这也就是android给我们提供的RemoteCallBack实现原理了,具体RemoteCallback怎么用,网上资料就比较多了,在此不再细说;