Android 跨进程双向通信(Messenger与AIDL)详解
今天这篇文章主要讲一下Messenger与AIDL的区别、优缺点以及各自的使用方法。
项目地址:https://github.com/libin7278/IPC
对binder和IPC还不熟悉的同学可以看一下之前的文章:
IPC进程间通信/跨进程通信
Android 中的Binder跨进程通信机制与AIDL
#Messenger与AIDL的异同
###一、Messenger与AIDL相同点
1.都与IPC的调用有关;
2.Messenger 是一种轻量级的 IPC方案,底层实现了AIDL,只是进行了封装,开发的时候不用再写.aidl文件。
3.都支持实时通信;
###二、Messenger与AIDL不同点
1.Messenger一次只能处理一个请求(串行)/AIDL一次可以处理多个请求(并行);
2.Messenger不支持RPC,只能通过message传递消息/AIDL支持RPC;
3.Messenger使用简单,轻量级,不需要创建AIDL文件/AIDL使用复杂,需要创建AIDL文件;
###三、Messenger与AIDL的优缺点及适用场景
名称 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
AIDL | 1.功能强大;2.支持实时通信;3.支持一对多并发通信;4.支持RPC(远程过程调用) | 1.使用复杂,需创建AIDL文件;2.需处理好线程同步问题 | 低并发的一对多即时通信,无RPC要求,不需要处理多线程) |
Messenger | 1.使用简单,轻量级;2.支持实时通信;3.支持一对多串行通信 | 1.功能简单;2.不支持RPC;3.数据通过message传输;4.不支持高并发场景;5.服务端想要回应客户端,必须通过Message的replyTo把服务端的Messenger传递过去 | 一对多且有RPC需求,想在服务里处理多线程的业务) |
##Messenger与AIDL的用法
###一、Messenger用法
####1、概述
Messenger进程间通信方式(如图):
我们可以在客户端发送一个Message给服务端,在服务端的handler中会接收到客户端的消息,然后进行对应的处理,处理完成后,再将结果等数据封装成Message,发送给客户端,客户端的handler中会接收到处理的结果。
server端:
收到的请求是放在Handler的MessageQueue里面,Handler大家都用过,它需要绑定一个Thread,然后不断poll message执行相关操作,这个过程是同步执行的。
client端:
client端要拿到返回值,需要把client的Messenger作为msg.replyTo参数传递过去,service端处理完之后,在调用客户端的Messenger的send(Message msg)方法把返回值传递回client
####2、实例
接下来我们看一下实例代码,一个服务端apk(MessengerServer),一个客户端apk(MessengerClient)。
=服务端=:
public class MessengerServer extends Service {private static final int MSG_FROM_CLIENT = 0x10001;private static final int MSG_TO_CLIENT = 0x10002;private static final String IS_LOGIN = "isLogin";private static final String NICK_NAME = "nickName";private static final String USER_ID = "userId";@SuppressLint("HandlerLeak")private Messenger mMessenger = new Messenger(new Handler() {@Overridepublic void handleMessage(Message msgfromClient) {Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息switch (msgfromClient.what) {//msg 客户端传来的消息case MSG_FROM_CLIENT:try {//模拟耗时Thread.sleep(2000);//传递数据Bundle toClicentDate = new Bundle();toClicentDate.putString(NICK_NAME,"张小可");toClicentDate.putBoolean(IS_LOGIN,true);toClicentDate.putInt(USER_ID,10086);msgToClient.setData(toClicentDate);msgToClient.what = MSG_TO_CLIENT;//传回ClientmsgfromClient.replyTo.send(msgToClient);} catch (InterruptedException e) {e.printStackTrace();} catch (RemoteException e) {e.printStackTrace();}break;}super.handleMessage(msgfromClient);}});@Nullable@Overridepublic IBinder onBind(Intent intent) {return mMessenger.getBinder();}
}
注册文件
<service android:name=".MessengerServer"><intent-filter><action android:name="android.intent.action.MESSENGER"/></intent-filter></service>
服务端就一个Service,可以看到代码相当的简单,只需要去声明一个Messenger对象,然后onBind方法返回mMessenger.getBinder();
这里我添加了sleep(2000)模拟耗时,注意在实际使用过程中,可以换成在独立开辟的线程中完成耗时操作,比如和HandlerThread结合使用。
=客户端=:
public class MainActivity extends AppCompatActivity {private static final int MSG_FROM_CLIENT = 0x10001;private static final int MSG_TO_CLIENT = 0x10002;private static final String IS_LOGIN = "isLogin";private static final String NICK_NAME = "nickName";private static final String USER_ID = "userId";private boolean isConn;private Messenger mService;private TextView tv_state;private TextView tv_message;private Button btn_send;@SuppressLint("HandlerLeak")private Messenger mMessenger = new Messenger(new Handler(){@SuppressLint("SetTextI18n")@Overridepublic void handleMessage(Message msgFromServer){switch (msgFromServer.what){case MSG_TO_CLIENT:Bundle data = msgFromServer.getData();tv_message.setText("服务器返回内容\n"+data.get(NICK_NAME)+"\n"+data.get(USER_ID)+"\n"+data.get(IS_LOGIN)+"\n");break;}super.handleMessage(msgFromServer);}});private ServiceConnection mConn = new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName name, IBinder service){mService = new Messenger(service);isConn = true;tv_state.setText("连接状态:connected!");}@Overridepublic void onServiceDisconnected(ComponentName name){mService = null;isConn = false;tv_state.setText("连接状态:disconnected!");}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_state = findViewById(R.id.tv_state);tv_message = findViewById(R.id.tv_message);btn_send = findViewById(R.id.btn_send);//开始绑定服务bindServiceInvoked();btn_send.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Message msgFromClient = new Message();msgFromClient.what = MSG_FROM_CLIENT;msgFromClient.replyTo = mMessenger;if (isConn){//往服务端发送消息try {mService.send(msgFromClient);} catch (RemoteException e) {e.printStackTrace();}}}});}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(mConn);}private void bindServiceInvoked(){Intent intent = new Intent();intent.setAction("android.intent.action.MESSENGER");bindService(intent, mConn, Context.BIND_AUTO_CREATE);}
}
首先bindService,然后在onServiceConnected中拿到回调的service(IBinder)对象,通过service对象去构造一个mService =new Messenger(service);然后就可以使用mService.send(msg)给服务端了。
我们看到在点击事件里面我们往服务端发送消息:
btn_send.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Message msgFromClient = new Message();msgFromClient.what = MSG_FROM_CLIENT;msgFromClient.replyTo = mMessenger;if (isConn){//往服务端发送消息try {mService.send(msgFromClient);} catch (RemoteException e) {e.printStackTrace();}}}});
那么服务端会收到消息,处理完成会将结果返回,传到Client端的mMessenger中的Handler的handleMessage方法中。
这样我们就实现了用messenger的双向通信,不过也发现我们前面说的问题,虽然使用简单,不用AIDL文件,但是不支持RPC,那么我们接下来看一下AIDL的用法。
###二、AIDL的用法
####1、概述
这里的Demo主要功能是在客户端发起登录,登出,服务端处理相应事件,之后将相应事件再回传给客户端。
这里需要先注册两个AIDL文件:
(这里的AIDL的文件相当于一个是客户端的,一个是服务端的)
IGuideAidlInterface 客户端调用服务端的相关接口
package messsage.binli.com.aidlserver;
import messsage.binli.com.aidlserver.IGuideListener;interface IGuideAidlInterface {void login(String userName , String passWord , String packageName); //登录void logout(String packageName); //登出boolean isLogin(); //是否登录void registerListener(in IGuideListener listener); //注册接口void unregisterListener(in IGuideListener listener); //解注册接口
}
IGuideListener 返回给客户端相应的处理结果
// IGuideListener.aidl
package messsage.binli.com.aidlserver;// Declare any non-default types here with import statementsinterface IGuideListener {void onLoginSuccess(String msg);void onLoginFail(String msg);void onLogoutSuccess(String msg);void onLogoutFail(String msg);
}
⚠️:客户端和服务端都需要AIDL文件且需要一致。把服务端生成的AIDL文件考入到客户端即可(路基必须保持和服务端一致),如图:
####2、实例
接下来我们看一下实例代码,一个服务端apk(AidlServer),一个客户端apk(AidlClient)。
代码非常简单就不详细讲解了。
服务端:
public class GuideServer extends Service{private IGuideListener iGuideListener;private IGuideAidlInterface.Stub mBinder = new IGuideAidlInterface.Stub() {@Overridepublic void login(final String userName, final String passWord, String packageName) throws RemoteException {new Thread(new Runnable() {@Overridepublic void run() {//模拟延迟任务try {Thread.sleep(2000);if (userName.equals("binli") && passWord.equals("123456")) {iGuideListener.onLogoutSuccess("登录成功: "+userName);} else {iGuideListener.onLoginFail("登录失败: "+userName);}} catch (InterruptedException e) {e.printStackTrace();} catch (RemoteException e) {e.printStackTrace();}}}).start();}@Overridepublic void logout(final String packageName) throws RemoteException {new Thread(new Runnable() {@Overridepublic void run() {//模拟延迟任务try {Thread.sleep(2000);if(packageName.equals("messsage.binli.com.aidlclient")){iGuideListener.onLogoutSuccess("登出成功!");}else{iGuideListener.onLogoutSuccess("登出失败!");}} catch (InterruptedException e) {e.printStackTrace();} catch (RemoteException e) {e.printStackTrace();}}}).start();}@Overridepublic boolean isLogin() throws RemoteException {return false;}@Overridepublic void registerListener(IGuideListener listener) throws RemoteException {if (listener != null) {iGuideListener = listener;}}@Overridepublic void unregisterListener(IGuideListener listener) throws RemoteException {if (listener != null) {iGuideListener = null;}}};@Overridepublic void onCreate() {super.onCreate();}@Overridepublic void onDestroy() {super.onDestroy();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return mBinder;}}
注册文件
<serviceandroid:name="messsage.binli.com.aidlserver.GuideServer"><intent-filter><action android:name="com.ecarx.membercenter.action.GUIDE"/></intent-filter></service>
客户端:
public class MainActivity extends AppCompatActivity {private TextView tv_style;private EditText et_accotun;private EditText et_password;private Button btn_login;private Button btn_logout;private IGuideAidlInterface iGuideAidlInterface;boolean isConnect;private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {iGuideAidlInterface = IGuideAidlInterface.Stub.asInterface(iBinder);tv_style.setText("当前状态:\n连接成功");isConnect = true;try {iGuideAidlInterface.registerListener(iGuideListener);} catch (Exception e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName componentName) {tv_style.setText("当前状态:\n连接断开");isConnect = false;}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_style = findViewById(R.id.tv_style);et_accotun = findViewById(R.id.et_accotun);et_password = findViewById(R.id.et_password);btn_login = findViewById(R.id.btn_login);btn_logout = findViewById(R.id.btn_logout);Intent intent = new Intent();intent.setAction("com.ecarx.membercenter.action.GUIDE");bindService(intent, serviceConnection, BIND_AUTO_CREATE);btn_login.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if(isConnect){try {Log.e("TAG","btn_login");iGuideAidlInterface.login(String.valueOf(et_accotun.getText()),String.valueOf(et_password.getText()) , getPackageName());} catch (RemoteException e) {e.printStackTrace();}}}});btn_logout.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if(isConnect){try {iGuideAidlInterface.logout(getPackageName());} catch (RemoteException e) {e.printStackTrace();}}}});}@Overrideprotected void onDestroy() {super.onDestroy();try {iGuideAidlInterface.unregisterListener(iGuideListener);} catch (RemoteException e) {e.printStackTrace();}unbindService(serviceConnection);}private IGuideListener iGuideListener = new IGuideListener.Stub() {@Overridepublic void onLoginSuccess(final String msg) throws RemoteException {runOnUiThread(new Runnable() {@Overridepublic void run() {tv_style.setText("当前状态:\n连接成功\n"+msg);}});Toast.makeText(MainActivity.this,"客户端登录成功回调====" + msg,Toast.LENGTH_LONG).show();}@Overridepublic void onLoginFail(final String msg) throws RemoteException {runOnUiThread(new Runnable() {@Overridepublic void run() {tv_style.setText("当前状态:\n连接成功\n"+msg);Toast.makeText(MainActivity.this,"客户端登录失败回调====" + msg,Toast.LENGTH_LONG).show();}});}@Overridepublic void onLogoutSuccess(final String msg) throws RemoteException {runOnUiThread(new Runnable() {@Overridepublic void run() {tv_style.setText("当前状态:\n连接成功\n"+msg);Toast.makeText(MainActivity.this,"客户端登出成功回调====" + msg,Toast.LENGTH_LONG).show();}});}@Overridepublic void onLogoutFail(final String msg) throws RemoteException {runOnUiThread(new Runnable() {@Overridepublic void run() {tv_style.setText("当前状态:\n连接成功\n"+msg);Toast.makeText(MainActivity.this,"客户端登出失败回调====" + msg,Toast.LENGTH_LONG).show();}});}};
}
项目地址:
https://github.com/libin7278/IPC
如果有帮助麻烦star一下 ~~
~~号外~~福利~~号外~~
程序员的福音: “老曾筋骨祛痛贴”,百年祖传配方,专治腰间盘、肩周、颈椎、坐骨神经、腰腿疼痛等,博主亲测效果非常棒,因长期久坐写代码,坐姿不规范导致脖子疼,腰椎疼,用过之后疼痛逐渐缓解,现在已无任何疼痛,用过后让你写代码一身轻松,so easy,妈妈再也不用担心我们写代码了。
购买链接: https://k.weidian.com/tja7GYzB
扫码下方二维码,关注公众号“伟大程序猿的诞生“,回复“膏药”领取优惠券
扫码关注公众号“伟大程序猿的诞生“,更多干货新鲜文章等着你~
公众号回复“资料获取”,获取更多干货哦~
公众号回复“膏药”,领取优惠券哦~
有问题添加本人微信号“fenghuokeji996” 或扫描博客导航栏本人二维码
Android 跨进程双向通信(Messenger与AIDL)详解相关推荐
- Android进阶笔记:Messenger源码详解
Messenger可以理解为一个是用于发送消息的一个类用法也很多,这里主要分析一下再跨进程的情况下Messenger的实现流程与源码分析.相信结合前面两篇关于aidl解析文章能够更好的对aidl有一个 ...
- Android跨进程通信Binder机制与AIDL实例
文章目录 进程通信 1.1 进程空间划分 1.2 跨进程通信IPC 1.3 Linux跨进程通信 1.4 Android进程通信 Binder跨进程通信 2.1 Binder简介 2.2 Binder ...
- Android进阶——AIDL详解之使用远程服务AIDL实现进程间带远程回调接口和自定义Bean的较复杂通信小结(二)
文章大纲 引言 一.远程回调AIDL接口的应用 1.封装基本的父类和一些工具类 2. 创建服务端的AIDL 2.1.定义回调AIDL接口 2.2.定义业务AIDL接口 3.实现服务端对应AIDL的带有 ...
- Android四大组件Service之AIDL详解
Android四大组件Service之AIDL详解 前言 简介 基础知识 AIDL 服务端 定义AIDL文件规则 创建 .aidl 文件 清单注册 通过 IPC 传递对象 调用 IPC 方法 Andr ...
- 【朝花夕拾】Android跨进程通信总结篇
前言 原文:https://www.cnblogs.com/andy-songwei/p/10256379.html 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一. ...
- 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10256379.html],谢谢! 只要是面试高级工程师岗位,Android跨进程通信就是最受面 ...
- 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇...
前言 原文:https://www.cnblogs.com/andy-songwei/p/10256379.html 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一. ...
- IPCInvoker,Android跨进程调用如此简单
一个APP为什么需要多条进程? 如果一条进程能够拥有足够多的资源,且不会被系统kill掉的话,让程序运行在一条进程上是最好的选择.但是系统资源是按进程来分配的,每条进程资源分配是有个上限的,而且当我们 ...
- Android 跨进程通信基础
2019独角兽企业重金招聘Python工程师标准>>> Android跨进程通信基础--Binder, BinderProxy, parcel, parcelable, Stub, ...
- Android 跨进程通信大总结
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/111553746 本文出自[赵彦军的博客] 文章目录 1.Android进程 2.修 ...
最新文章
- 动态指定超链接参数的几种方法(Passing a JavaScript variable into href of )
- lte 中crs_LTE网络CRS功率配置及其影响研究
- 浅谈equals和==的区别
- 吴恩达给 74 岁老父亲发证了!8 年完成 146 门课程!
- 使用备用访问映射改变站点访问路径
- PostgreSQL 统计信息pg_statistic格式及导入导出dump_stat - 兼容Oracle
- vue render函数_Vue原理解析(一):Vue到底是什么?
- sql azure 语法_如何:Azure中SQL Server文件快照备份
- 在K3凭证处理中的部份实用操作
- 使用cmake和vs2019进行编译libtorch过程
- 让APP不被android系统杀掉
- 02 - Tomcat配置
- python如何设置双索引_python-在新的多索引下串联熊猫列
- 鸿蒙 悟空遥控,利用悟空遥控推送软件,成功实现高德地图等三方APP装
- 新版眼保健操图解(转)
- 闰年c语言循环计算方法,C语言计算有多少闰年(答案原创)
- android组件化管理单例,Pigeon——支持增量编译和组件化开发的路由框架
- 使用rsync来实现文件同步
- 【Origin】Origin准确标注某点
- variant 类型