1.Messenger 我们知道,Service通过IBinder可以实现同一应用内同一进程的通信,而不同进程间的通信,最简单的方式就是使用Messenger服务提供通信接口。利用Messenger,我们无需使用AIDL便可执行进程间通信 (IPC)。 首先看一下Messenger的定义:允许实现基于消息的进程间通信的方式。 以下是Messenger使用的主要步骤: ①服务Service实现一个Handler,由其接收来自客户端的每个调用的回调; ②Handler用于创建Messenger对象(对Handler的引用); ③Messenger创建一个IBinder,服务通过onBind()使其返回客户端; ④客户端使用IBinder将Messenger(引用服务的 Handler)实例化,然后使用Messenger将 Message对象发送给服务; ⑤服务在其Handler中(在 handleMessage() 方法中)接收每个Message;
2.举例(同一应用不同进程) 以下是一个使用Messenger接口的简单服务示例。 服务端进程实现如下: public class MessengerService extends Service{ static final int MSG_SAY_HELLO = 1;
//用于接收从客户端传递过来的数据 class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Log.i(TAG, “thanks,Service had receiver message from client!”); //模拟处理耗时任务 Thread.sleep(2000); break; default: super.handleMessage(msg); } } }
//创建Messenger,并传入Handler实例对象 final Messenger mMessenger = new Messenger(new IncomingHandler());
//绑定Service时,该方法被调用,通过Messenger返回一个实现IBinder接口的实例对象 @Override public IBinder onBind(Intent intent) { Log.i(TAG, “Service is invoke onBind”); return mMessenger.getBinder(); } } 首先我们需要创建一个服务类MessengerService继承自Service,同时创建一个继承自Handler的IncomingHandler对象来接收客户端进程发送过来的消息,并通过其handleMessage(Message msg)进行消息处理。接着通过IncomingHandler对象创建一个Messenger对象,该对象是与客户端交互的特殊对象,然后在Service的onBind中返回这个Messenger对象的底层Binder即可。 下面看看客户端进程的实现: public class ActivityMessenger extends Activity{ //与服务端交互的Messenger Messenger mService = null; ///定义一个标志位,标志绑定服务是否成功 boolean mBound;
private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected (ComponentName className, IBinder service) { //通过服务端传递的IBinder对象,创建相应的Messenger,通过该Messenger对象与服务端进行交互 mService = new Messenger(service); mBound = true; }
public void onServiceDisconnected (ComponentName className) { mService = null; mBound = false; } };
public void sayHello(View v) { if (!mBound) return; // 创建与服务交互的消息实体Message Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try { //发送消息 mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_msger);
//绑定服务 bindServiceBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(“zj”,“onClick–>bindService”); //当前Activity绑定服务端 bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } });
//发送消息给服务端 sendMsgBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sayHello(v); } });
//解绑 unbindServiceBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mBound) { Log.d(“zj”,“onClick–>unbindService”); unbindService(mConnection); mBound = false; } } }); } } 在客户端进程中,创建一个ServiceConnection对象,该对象代表与服务端的链接,当调用bindService()将当前Activity绑定到MessengerService时,onServiceConnected方法被调用,利用服务端传递过来的底层Binder对象构造出与服务端交互的Messenger对象,接着创建与服务交互的消息实体Message,将要发生的信息封装在Message中,并通过Messenger实例对象发送给服务端。 最后,不要忘记在清单文件声明Service。 由于要测试不同进程的交互,则需要将Service放在单独的进程中,因此Service声明如下: < service android:name=".messenger.MessengerService" android:process=":remote" /> 其中android:process=":remote"代表该Service在单独的进程中创建。 运行程序,多次点击绑定服务,然后发送信息给服务端,最后解除绑定,Log打印如下: 通过上述例子可知Service服务端确实收到了客户端发送的信息,而且在Messenger中进行数据传递必须将数据封装到Message中,因为Message和Messenger都实现了Parcelable接口,可以轻松跨进程传递数据,而Message可以传递的信息载体有what,arg1,arg2,Bundle以及replyTo,至于object字段,对于同一进程中的数据传递确实很实用,但对于进程间的通信,则显得相当尴尬,在android2.2前,object不支持跨进程传输,但即便是android2.2之后也只能传递android系统提供的实现了Parcelable接口的对象,也就是说我们通过自定义实现Parcelable接口的对象无法通过object字段来传递,因此object字段的实用性在跨进程中也变得相当低了。不过所幸我们还有Bundle对象,Bundle可以支持大量的数据类型。 从Log我们也看出无论是使用拓展Binder类的实现方式还是使用Messenger的实现方式,Service的生命周期方法的调用顺序是一样的,即onCreate、onBind、onUnBind、onDestroy,而且多次绑定中也只有第一次时才调用onBind()。
以上的例子演示了如何在服务端接收客户端发送的消息,但有时候我们可能还需要服务端能回应客户端,这时便需要提供双向消息传递了,下面就来实现一个服务端与客户端双向消息传递的简单例子。 ?先来看看服务端的修改,在服务端,我们只需修改IncomingHandler,收到消息后,给客户端回复一条信息。 //用于接收从客户端传递过来的数据 class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Log.i(TAG, “thanks,Service had receiver message from client!”); //回复客户端信息,该对象由客户端传递过来 Messenger client=msg.replyTo; //获取回复信息的消息实体 Message replyMsg=Message.obtain(null,MessengerService.MSG_SAY_HELLO); Bundle bundle=new Bundle(); bundle.putString(“reply”,"ok~,I had receiver message from you! "); replyMsg.setData(bundle); //向客户端发送消息 try { client.send(replyMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } 接着修改客户端,为了接收服务端的回复,客户端也需要一个接收消息的Messenger和Handler,其实现如下: //用于接收服务器返回的信息 private Messenger mRecevierReplyMsg= new Messenger(new ReceiverReplyMsgHandler());
private static class ReceiverReplyMsgHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what) { //接收服务端回复 case MessengerService.MSG_SAY_HELLO: Log.i(TAG, “receiver message from service:”+msg.getData().getString(“reply”)); break; default: super.handleMessage(msg); } } } 除了添加以上代码,还需要在发送信息时把接收服务器端回复的Messenger通过Message的replyTo参数传递给服务端,以便作为同学桥梁,代码如下: public void sayHello(View v) { if (!mBound) return; // 创建与服务交互的消息实体Message Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); //把接收服务器端回复的Messenger通过Message的replyTo参数传递给服务端 msg.replyTo=mRecevierReplyMsg; try { //发送消息 mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } 到此服务端与客户端双向消息传递的简单例子修改完成,我们运行一下代码,看看Log打印,如下: 由Log可知,服务端和客户端确实各自收到了信息,到此我们就把采用Messenger进行跨进程通信的方式分析完了,最后为了辅助大家理解,这里提供一张通过Messenger方式进行进程间通信的原理图:
3.举例(不同应用) 上一个例子是在同一应用的不同进程之间利用Messenger进行进程间通信,当然还可以在不同应用之间使用Mesenger。  可以看到,我们可以在客户端发送一个Message给服务端,在服务端的handler中会接收到客户端的消息,然后进行对应的处理,处理完成后,再将结果等数据封装成Message,发送给客户端,客户端的handler中会接收到处理的结果。
这个例子,通过两个apk演示,一个apk是Server端,一个是Client端。 先看Server端代码: public class MessengerService extends Service{ private static final int MSG_SUM = 0x110; private Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msgfromClient) { Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息 switch (msgfromClient.what) { //msg 客户端传来的消息 case MSG_SUM: msgToClient.what = MSG_SUM; try { //模拟耗时 Thread.sleep(2000); msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2; msgfromClient.replyTo.send(msgToClient); } catch (InterruptedException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } break; } super.handleMessage(msgfromClient); } });
@Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } } 服务端就一个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> 写完以后,直接运行安装apk。
接下来看另一个app(客户端): public class MainActivity extends Activity { private static final int MSG_SUM = 0x110; private Button mBtnAdd; private LinearLayout mLyContainer; private TextView mTvState;//显示连接状态 private Messenger mService; private boolean isConn;
private Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msgFromServer) { switch (msgFromServer.what) { case MSG_SUM: TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1);//点击按钮发送消息时动态创建的textview,并设置了id,这里可以根据id查找 tv.setText(tv.getText() + “=>” + msgFromServer.arg2); break; } super.handleMessage(msgFromServer); } });
private ServiceConnection mConn = new ServiceConnection() { @Override public void onServiceConnected (ComponentName name, IBinder service) { mService = new Messenger(service); isConn = true; mTvState.setText(“connected!”); }
@Override public void onServiceDisconnected (ComponentName name) { mService = null; isConn = false; mTvState.setText(“disconnected!”); } };
private int mA; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //开始绑定服务 bindServiceInvoked();
mTvState = findViewById(R.id.tv_callback); mBtnAdd = findViewById(R.id.btn_add); mLyContainer =findViewById(R.id.ll_container);
mBtnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { int a = mA++; int b = (int) (Math.random() * 100); //创建一个tv,添加到LinearLayout中 TextView tv = new TextView(this); tv.setText(a + " + " + b + " = caculating …"); tv.setId(a); mLyContainer.addView(tv);
//每点一次按钮就向service发送一次请求 Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); msgFromClient.replyTo = mMessenger; if (isConn) { //往服务端发送消息 mService.send(msgFromClient); } } catch (RemoteException e) { e.printStackTrace(); } } }); }
private void bindServiceInvoked() { Intent intent = new Intent(); intent.setAction(“com.zhy.aidl.calc”); bindService(intent, mConn, Context.BIND_AUTO_CREATE); Log.e(TAG, “bindService invoked !”); }
@Override protected void onDestroy() { super.onDestroy(); unbindService(mConn); } } 客户端首先bindService()绑定服务,然后在onServiceConnected中拿到回调的service(IBinder)对象,通过service对象去构造一个mService =new Messenger(service);然后就可以使用mService.send(msg)给服务端了。服务端会收到消息,处理完成后会将结果返回,传到Client端的mMessenger中的Handler的handleMessage方法中。 效果图:  可以看到,我们每点击一次按钮,就往服务器发送一个消息,服务器拿到消息执行完成后,将结果返回。 通过代码可以看到服务端往客户端传递数据是通过msg.replyTo这个对象的。那么服务端完全可以做到,使用一个List甚至Map去存储所有绑定的客户端的msg.replyTo对象,然后想给谁发消息都可以。甚至可以把A进程发来的消息,通过B进程的msg.replyTo发到B进程那里去。
4.Messenger的源码分析 其实Messenger的内部实现,实际上也是依赖于aidl文件实现的。 ①客户端向服务端通信 服务端的onBind是这么写的: public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } 那么点进去: public IBinder getBinder() { return mTarget.asBinder(); } 可以看到返回的是mTarget.asBinder(); mTarget是哪来的呢? 别忘了我们前面去构造mMessenger对象的代码:new Messenger(new Handler()); public Messenger(Handler target) { mTarget = target.getIMessenger(); } 原来是Handler返回的,我们继续跟进去: final IMessenger getIMessenger() { synchronized (mQueue) { if (mMessenger != null) { return mMessenger; } mMessenger = new MessengerImpl(); return mMessenger; } }
private final class MessengerImpl extends IMessenger.Stub { public void send(Message msg) { msg.sendingUid = Binder.getCallingUid(); Handler.this.sendMessage(msg); } } 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: package android.os; import android.os.Message; /** @hide */ oneway interface IMessenger { void send(in Message msg); } 看到这,你应该明白了,Messenger并没有什么神奇之处,实际上,就是依赖该aidl文件生成的类,继承了IMessenger.Stub类,实现了send方法,send方法中参数会通过客户端传递过来,最终发送给handler进行处理。
客户端首先通过onServiceConnected拿到sevice(Ibinder)对象,这里没什么特殊的,我们平时的写法也是这样的,只不过我们平时会这么写:IMessenger.Stub.asInterface(service)拿到接口对象进行调用;而,我们的代码中是mService = new Messenger(service); 跟进去,你会发现: public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); } 和我们平时的写法一模一样!
到这里就可以明白,客户端与服务端通信,实际上和我们平时的写法没有任何区别,通过编写aidl文件,服务端onBind利用Stub编写接口实现返回;客户端利用回调得到的IBinder对象,使用IMessenger.Stub.asInterface(target)拿到接口实例进行调用。 ②服务端与客户端通信 上个例子中,客户端send方法发送的是一个Message,这个Message.replyTo指向的是一个mMessenger,我们在Activity中初始化的。 那么将消息发送到服务端,肯定是通过序列化与反序列化拿到Message对象,我们看下Message的反序列化的代码: #Message private void readFromParcel(Parcel source) { what = source.readInt(); arg1 = source.readInt(); arg2 = source.readInt(); if (source.readInt() != 0) { obj = source.readParcelable(getClass().getClassLoader()); } when = source.readLong(); data = source.readBundle(); replyTo = Messenger.readMessengerOrNullFromParcel(source); sendingUid = source.readInt(); } 主要看replyTo,调用的是Messenger.readMessengerOrNullFromParcel: public static Messenger readMessengerOrNullFromParcel(Parcel in) { IBinder b = in.readStrongBinder(); return b != null ? new Messenger(b) : null; }
public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) { out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder() : null); } 通过上面的writeMessengerOrNullToParcel可以看到,它将客户端的messenger.mTarget.asBinder()对象进行了恢复,客户端的message.mTarget.asBinder()是什么? 客户端也是通过Handler创建的Messenger,于是asBinder返回的是: public Messenger(Handler target) { mTarget = target.getIMessenger(); } final IMessenger getIMessenger() { synchronized (mQueue) { if (mMessenger != null) { return mMessenger; } mMessenger = new MessengerImpl(); return mMessenger; } }
private final class MessengerImpl extends IMessenger.Stub { public void send(Message msg) { msg.sendingUid = Binder.getCallingUid(); Handler.this.sendMessage(msg); } }
public IBinder getBinder() { return mTarget.asBinder(); } 那么asBinder,实际上就是MessengerImpl extends IMessenger.Stub中的asBinder了。 #IMessenger.Stub @Override public android.os.IBinder asBinder() { return this; } 那么其实返回的就是MessengerImpl对象自己。到这里可以看到message.mTarget.asBinder()其实返回的是客户端的MessengerImpl对象。 最终,发送给客户端的代码是这么写的: msgfromClient.replyTo.send(msgToClient); send源码如下: public void send(Message message) throws RemoteException { mTarget.send(message); } 这个mTarget实际上就是对客户端的MessengerImpl对象的封装,那么send(message)(屏蔽了transact/onTransact的细节),这个message最终肯定传到客户端的handler的handleMessage方法中。
总结下Mesenger: ①客户端与服务端通信,利用的aidl文件,没什么特殊的。 ②服务端与客户端通信,主要是在传输的消息上做了处理,让Messager.replyTo指向的客户端的Messenger,而Messenger又持有客户端的一个Binder对象(MessengerImpl)。服务端正是利用这个Binder对象做的与客户端的通信。
|