Android Messenger
说到Android进程间通信,大家肯定能想到的是编写aidl文件,然后通过aapt生成的类方便的完成服务端,以及客户端代码的编写。不熟悉的话可以查看Android aidl Binder框架浅析;
当然今天要说的通信方式肯定不是通过编写aidl文件的方式,今天要讲Messenger
。
This allows for the implementation of message-based communication across processes
允许实现基于消息的进程间通信的方式。
那么,什么叫基于消息的进程间通信方式呢?看个图理解下:
可以看到,我们可以在客户端发送一个Message给服务端,在服务端的handler中会接收到客户端的消息,然后进行对应的处理,处理完成后,再将结果等数据封装成Message,发送给客户端,客户端的handler中会接收到处理的结果。
这样的进程间通信是不是很爽呢?
- 基于Message,相信大家都很熟悉
- 支持回调的方式,也就是服务端处理完成长任务可以和客户端交互
- 不需要编写aidl文件
此外,还支持,记录客户端对象的Messenger,然后可以实现一对多的通信;甚至作为一个转接处,任意两个进程都能通过服务端进行通信,这个后面再说。
看到这,有没有一些的小激动,我们可以不写aidl文件,方便的实现进程间通信了,是不是又可以装一下了。哈,下面看个简单的例子。
二、通信实例
这个例子,通过两个apk演示,一个apk是Server端,一个是Client端;
(1) Server端
代码
1 package com.imooc.messenger_server; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Handler; 6 import android.os.IBinder; 7 import android.os.Message; 8 import android.os.Messenger; 9 import android.os.RemoteException; 10 11 public class MessengerService extends Service 12 { 13 14 private static final int MSG_SUM = 0x110; 15 16 //最好换成HandlerThread的形式 17 private Messenger mMessenger = new Messenger(new Handler() 18 { 19 @Override 20 public void handleMessage(Message msgfromClient) 21 { 22 Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息 23 switch (msgfromClient.what) 24 { 25 //msg 客户端传来的消息 26 case MSG_SUM: 27 msgToClient.what = MSG_SUM; 28 try 29 { 30 //模拟耗时 31 Thread.sleep(2000); 32 msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2; 33 msgfromClient.replyTo.send(msgToClient); 34 } catch (InterruptedException e) 35 { 36 e.printStackTrace(); 37 } catch (RemoteException e) 38 { 39 e.printStackTrace(); 40 } 41 break; 42 } 43 44 super.handleMessage(msgfromClient); 45 } 46 }); 47 48 @Override 49 public IBinder onBind(Intent intent) 50 { 51 return mMessenger.getBinder(); 52 } 53 }
服务端就一个Service,可以看到代码相当的简单,只需要去声明一个Messenger
对象,然后onBind方法返回mMessenger.getBinder();
然后坐等客户端将消息发送到handleMessage想法,根据message.what去判断进行什么操作,然后做对应的操作,最终将结果通过 msgfromClient.replyTo.send(msgToClient);返回。
可以看到我们这里主要是取出客户端传来的两个数字,然后求和返回,这里我有意添加了sleep(2000)模拟耗时,注意在实际使用过程中,可以换成在独立开辟的线程中完成耗时操作,比如和HandlerThread结合使用。
注册文件
<service android:name=".MessengerService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.zhy.aidl.calc"></action> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
别忘了注册service,写完以后直接安装。
(二)客户端
Activity
1 package com.imooc.messenger_client; 2 3 import android.content.ComponentName; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.Handler; 9 import android.os.IBinder; 10 import android.os.Message; 11 import android.os.Messenger; 12 import android.os.RemoteException; 13 import android.support.v7.app.AppCompatActivity; 14 import android.util.Log; 15 import android.view.View; 16 import android.widget.Button; 17 import android.widget.LinearLayout; 18 import android.widget.TextView; 19 20 public class MainActivity extends AppCompatActivity 21 { 22 private static final String TAG = "MainActivity"; 23 private static final int MSG_SUM = 0x110; 24 25 private Button mBtnAdd; 26 private LinearLayout mLyContainer; 27 //显示连接状态 28 private TextView mTvState; 29 30 private Messenger mService; 31 private boolean isConn; 32 33 34 private Messenger mMessenger = new Messenger(new Handler() 35 { 36 @Override 37 public void handleMessage(Message msgFromServer) 38 { 39 switch (msgFromServer.what) 40 { 41 case MSG_SUM: 42 TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1); 43 tv.setText(tv.getText() + "=>" + msgFromServer.arg2); 44 break; 45 } 46 super.handleMessage(msgFromServer); 47 } 48 }); 49 50 51 private ServiceConnection mConn = new ServiceConnection() 52 { 53 @Override 54 public void onServiceConnected(ComponentName name, IBinder service) 55 { 56 mService = new Messenger(service); 57 isConn = true; 58 mTvState.setText("connected!"); 59 } 60 61 @Override 62 public void onServiceDisconnected(ComponentName name) 63 { 64 mService = null; 65 isConn = false; 66 mTvState.setText("disconnected!"); 67 } 68 }; 69 70 private int mA; 71 72 @Override 73 protected void onCreate(Bundle savedInstanceState) 74 { 75 super.onCreate(savedInstanceState); 76 setContentView(R.layout.activity_main); 77 78 //开始绑定服务 79 bindServiceInvoked(); 80 81 mTvState = (TextView) findViewById(R.id.id_tv_callback); 82 mBtnAdd = (Button) findViewById(R.id.id_btn_add); 83 mLyContainer = (LinearLayout) findViewById(R.id.id_ll_container); 84 85 mBtnAdd.setOnClickListener(new View.OnClickListener() 86 { 87 @Override 88 public void onClick(View v) 89 { 90 try 91 { 92 int a = mA++; 93 int b = (int) (Math.random() * 100); 94 95 //创建一个tv,添加到LinearLayout中 96 TextView tv = new TextView(MainActivity.this); 97 tv.setText(a + " + " + b + " = caculating ..."); 98 tv.setId(a); 99 mLyContainer.addView(tv); 100 101 Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); 102 msgFromClient.replyTo = mMessenger; 103 if (isConn) 104 { 105 //往服务端发送消息 106 mService.send(msgFromClient); 107 } 108 } catch (RemoteException e) 109 { 110 e.printStackTrace(); 111 } 112 } 113 }); 114 115 } 116 117 private void bindServiceInvoked() 118 { 119 Intent intent = new Intent(); 120 intent.setAction("com.zhy.aidl.calc"); 121 bindService(intent, mConn, Context.BIND_AUTO_CREATE); 122 Log.e(TAG, "bindService invoked !"); 123 } 124 125 @Override 126 protected void onDestroy() 127 { 128 super.onDestroy(); 129 unbindService(mConn); 130 } 131 132 133 }
代码也不复杂,首先bindService,然后在onServiceConnected中拿到回调的service(IBinder)对象,通过service对象去构造一个mService =new Messenger(service);
然后就可以使用mService.send(msg)给服务端了。
我们消息的发送在Btn.onclick里面:
1 Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); 2 msgFromClient.replyTo = mMessenger; 3 if (isConn) 4 { 5 //往服务端发送消息 6 mService.send(msgFromClient); 7 }
那么服务端会收到消息,处理完成会将结果返回,传到Client端的mMessenger中的Handler的handleMessage方法中。
布局文件
1 <LinearLayout android:id="@+id/id_ll_container" 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:orientation="vertical" 7 android:paddingBottom="@dimen/activity_vertical_margin" 8 android:paddingLeft="@dimen/activity_horizontal_margin" 9 android:paddingRight="@dimen/activity_horizontal_margin" 10 android:paddingTop="@dimen/activity_vertical_margin" 11 tools:context=".MainActivity"> 12 13 <TextView 14 android:id="@+id/id_tv_callback" 15 android:layout_width="match_parent" 16 android:layout_height="wrap_content" 17 android:text="Messenger Test!"/> 18 19 <Button android:id="@+id/id_btn_add" 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" 22 android:text="add"/> 23 24 </LinearLayout>
效果图
可以看到,我们每点击一次按钮,就往服务器发送个消息,服务器拿到消息执行完成后,将结果返回。
整个通信的代码看起来还是相当的清爽的,那么大家有没有对其内部的原理有一丝的好奇呢?下面我们就来看下其内部是如何实现的。
对了,源码分析前,这里插一句,大家通过代码可以看到服务端往客户端传递数据是通过msg.replyTo这个对象的。那么服务端完全可以做到,使用一个List甚至Map去存储所有绑定的客户端的msg.replyTo对象,然后想给谁发消息都可以。甚至可以把A进程发来的消息,通过B进程的msg.replyTo发到B进程那里去。相关代码呢,可以参考官方的文档:service,注意下拉找:Remote Messenger Service Sample。
三、源码分析
其实Messenger的内部实现的,实际上也是依赖于aidl文件实现的。
(一)首先我们看客户端向服务端通信
服务端
服务端的onBind是这么写的:
1 public IBinder onBind(Intent intent) 2 { 3 return mMessenger.getBinder(); 4 }
那么点进去:
1 public IBinder getBinder() { 2 return mTarget.asBinder(); 3 }
可以看到返回的是mTarget.asBinder();
mTarget是哪来的呢?
别忘了我们前面去构造mMessenger对象的代码:new Messenger(new Handler())
;
1 public Messenger(Handler target) { 2 mTarget = target.getIMessenger(); 3 }
原来是Handler返回的,我们继续跟进去
1 final IMessenger getIMessenger() { 2 synchronized (mQueue) { 3 if (mMessenger != null) { 4 return mMessenger; 5 } 6 mMessenger = new MessengerImpl(); 7 return mMessenger; 8 } 9 } 10 11 private final class MessengerImpl extends IMessenger.Stub { 12 public void send(Message msg) { 13 msg.sendingUid = Binder.getCallingUid(); 14 Handler.this.sendMessage(msg); 15 } 16 }
mTarget是一个MessengerImpl对象,那么asBinder实际上是返回this,也就是MessengerImpl对象;
这是个内部类,可以看到继承自IMessenger.Stub,然后实现了一个send方法,该方法就是将接收到的消息通过 Handler.this.sendMessage(msg);发送到handleMessage方法。
看到这,大家有没有想到什么,难道不觉得extends IMessenger.Stub这种写法异常的熟悉么?
我们传统写aidl文件,aapt给我们生成什么,生成IXXX.Stub类,然后我们服务端继承IXXX.Stub实现接口中的方法。
没错,其实这里内部其实也是依赖一个aidl生成的类,这个aidl位于:frameworks/base/core/java/android/os/IMessenger.aidl
.
1 package android.os; 2 3 import android.os.Message; 4 5 /** @hide */ 6 oneway interface IMessenger { 7 void send(in Message msg); 8 }
看到这,你应该明白了,Messenger并没有什么神奇之处,实际上,就是依赖该aidl文件生成的类,继承了IMessenger.Stub类,实现了send方法,send方法中参数会通过客户端传递过来,最终发送给handler进行处理。这里不理解,请详细看下Android aidl Binder框架浅析;
客户端
客户端首先通过onServiceConnected拿到sevice(Ibinder)对象,这里没什么特殊的,我们平时的写法也是这样的,只不过我们平时会这么写:
IMessenger.Stub.asInterface(service)拿到接口对象进行调用;
而,我们的代码中是
mService = new Messenger(service);
跟进去,你会发现:
1 public Messenger(IBinder target) { 2 mTarget = IMessenger.Stub.asInterface(target); 3 }
soga,和我们平时的写法一模一样!
到这里就可以明白,客户端与服务端通信,实际上和我们平时的写法没有任何区别,通过编写aidl文件,服务端onBind利用Stub编写接口实现返回;客户端利用回调得到的IBinder对象,使用IMessenger.Stub.asInterface(target)拿到接口实例进行调用(内部实现,参考Android aidl Binder框架浅析)。
(2)服务端与客户端通信
那么,客户端与服务端通信的确没什么特殊的地方,我们完全也可以编写个类似的aidl文件实现;那么服务端是如何与客户端通信的呢?
还记得,客户端send方法发送的是一个Message,这个Message.replyTo指向的是一个mMessenger,我们在Activity中初始化的。
那么将消息发送到服务端,肯定是通过序列化与反序列化拿到Message对象,我们看下Message的反序列化的代码:
1 # Message 2 3 private void readFromParcel(Parcel source) { 4 what = source.readInt(); 5 arg1 = source.readInt(); 6 arg2 = source.readInt(); 7 if (source.readInt() != 0) { 8 obj = source.readParcelable(getClass().getClassLoader()); 9 } 10 when = source.readLong(); 11 data = source.readBundle(); 12 replyTo = Messenger.readMessengerOrNullFromParcel(source); 13 sendingUid = source.readInt(); 14 }
主要看replyTo,调用的是Messenger.readMessengerOrNullFromParcel
1 public static Messenger readMessengerOrNullFromParcel(Parcel in) { 2 IBinder b = in.readStrongBinder(); 3 return b != null ? new Messenger(b) : null; 4 } 5 6 public static void writeMessengerOrNullToParcel(Messenger messenger, 7 Parcel out) { 8 out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder() 9 : null); 10 }
通过上面的writeMessengerOrNullToParcel可以看到,它将客户端的messenger.mTarget.asBinder()对象进行了恢复,客户端的message.mTarget.asBinder()是什么?
客户端也是通过Handler创建的Messenger,于是asBinder返回的是:
1 public Messenger(Handler target) { 2 mTarget = target.getIMessenger(); 3 } 4 final IMessenger getIMessenger() { 5 synchronized (mQueue) { 6 if (mMessenger != null) { 7 return mMessenger; 8 } 9 mMessenger = new MessengerImpl(); 10 return mMessenger; 11 } 12 } 13 14 private final class MessengerImpl extends IMessenger.Stub { 15 public void send(Message msg) { 16 msg.sendingUid = Binder.getCallingUid(); 17 Handler.this.sendMessage(msg); 18 } 19 } 20 21 public IBinder getBinder() { 22 return mTarget.asBinder(); 23 }
那么asBinder,实际上就是MessengerImpl extends IMessenger.Stub
中的asBinder了。
1 #IMessenger.Stub 2 3 @Override 4 public android.os.IBinder asBinder() 5 { 6 return this; 7 }
那么其实返回的就是MessengerImpl对象自己。到这里可以看到message.mTarget.asBinder()其实返回的是客户端的MessengerImpl对象。
最终,发送给客户端的代码是这么写的:
1 msgfromClient.replyTo.send(msgToClient); 2 3 public void send(Message message) throws RemoteException { 4 mTarget.send(message); 5 }
这个mTarget实际上就是对客户端的MessengerImpl对象的封装,那么send(message)(屏蔽了transact/onTransact的细节),这个message最终肯定传到客户端的handler的handleMessage方法中。
好了,到此我们的源码分析就结束了~~
总结下:
- 客户端与服务端通信,利用的aidl文件,没什么特殊的
- 服务端与客户端通信,主要是在传输的消息上做了处理,让Messager.replyTo指向的客户端的Messenger,而Messenger又持有客户端的一个Binder对象(MessengerImpl)。服务端正是利用这个Binder对象做的与客户端的通信。
可以考虑自己编写aidl文件,实现下服务端对客户端的回调。