首页 > 代码库 > Android-Messenger跨进程通信
Android-Messenger跨进程通信
http://blog.csdn.net/lmj623565791/article/details/47017485
一.概述
我们可以在客户端发送一个Message给服务端,在服务端的handler中会接收到客户端的消息,然后进行对应的处理,处理完成后,再将结果等数据封装成Message,发送给客户端,客户端的handler中会接收到处理的结果。
有这么几个特点:
-
基于Message,相信大家都很熟悉
-
支持回调的方式,也就是服务端处理完成长任务可以和客户端交互
-
不需要编写aidl文件
此外,还支持,记录客户端对象的Messenger,然后可以实现一对多的通信;甚至作为一个转接处,任意两个进程都能通过服务端进行通信,这个后面再说。
二.应用
(1) Server端
package com.imooc.messenger_server; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; public class MessengerService extends Service { private static final int MSG_SUM = 0x110; //最好换成HandlerThread的形式 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>
(二)客户端
Activity
package com.imooc.messenger_client; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; 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); 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 = (TextView) findViewById(R.id.id_tv_callback); mBtnAdd = (Button) findViewById(R.id.id_btn_add); mLyContainer = (LinearLayout) findViewById(R.id.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(MainActivity.this); tv.setText(a + " + " + b + " = caculating ..."); tv.setId(a); mLyContainer.addView(tv); 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)给服务端了。
这里插一句,大家通过代码可以看到服务端往客户端传递数据是通过msg.replyTo这个对象的。那么服务端完全可以做到,使用一个List甚至Map去存储所有绑定的客户端的msg.replyTo对象,然后想给谁发消息都可以。甚至可以把A进程发来的消息,通过B进程的msg.replyTo发到B进程那里去。相关代码呢,可以参考官方的文档:service,注意下拉找:Remote Messenger Service Sample。
三.原理
源码解析那篇文章里有非常详细的分析,这里给出我的个人理解:
先简单总结一下应用流程:
- 服务器端new Messenger(new Handler()),然后在Handler里定义handleMessage()接收和处理客户端传来的message,最后通过message.replyto.send()把结果发送回去
- 客户端new Messenger(new Handler()),然后在Handler里定义handleMessage()接收和处理服务器端传来的message。实现ServiceConnection类,并在onServiceConnected里用mService = new Messenger(service)得到远程接口,bindService连接后用mService.send发送数据给服务器端
(一)客户端向服务端通信
我们直接看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); } }
其实看到这就知道了,Messenger内部也是用AIDL实现的
下面就直接用AIDL来理解Messenger:
- 首先是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); }
- 然后要在stub中实现这个方法,在onbind中返回stub
private final class MessengerImpl extends IMessenger.Stub { public void send(Message msg) { msg.sendingUid = Binder.getCallingUid(); Handler.this.sendMessage(msg); } }
和AIDL一样,服务端stub的方法是给客户端调用的,客户端利用AIDL把msg传来,再调用send方法交给服务器端的handleMessage处理
客户端
得到服务器端的IBinder对象: mService = new Messenger(service);
public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }
(二)服务端与客户端通信
客户端向服务器端发送消息:
Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); msgFromClient.replyTo = mMessenger; if (isConn) { //往服务端发送消息 mService.send(msgFromClient); }
客户端利用AIDL把msg传过去,再调用send方法交给服务器端的handleMessage处理
服务器接收并处理消息:
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); }
取出客户端发过来的message,处理后用message.replyTo.send()就可以直接把结果发回去
这里的replyTo是客户端指定的
msgFromClient.replyTo = mMessenger;
mMessenger就是客户端的Messenger对象。服务器端在反序列化客户端传来的message时,会通过这个replyTo得到客户端的MessengerImpl对象。
换句话说,服务器端通过客户端的stub里的send方法,把message结果发送回去,最后在客户端的handlerMessage里处理
总结下:
-
客户端与服务端通信,利用的aidl文件,没什么特殊的
-
服务端与客户端通信,主要是在传输的消息上做了处理,让Messager.replyTo指向的客户端的Messenger,而Messenger又持有客户端的一个Binder对象(MessengerImpl)。服务端正是利用这个Binder对象做的与客户端的通信。
Android-Messenger跨进程通信