今天这篇文章主要讲一下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)详解相关推荐

  1. Android进阶笔记:Messenger源码详解

    Messenger可以理解为一个是用于发送消息的一个类用法也很多,这里主要分析一下再跨进程的情况下Messenger的实现流程与源码分析.相信结合前面两篇关于aidl解析文章能够更好的对aidl有一个 ...

  2. Android跨进程通信Binder机制与AIDL实例

    文章目录 进程通信 1.1 进程空间划分 1.2 跨进程通信IPC 1.3 Linux跨进程通信 1.4 Android进程通信 Binder跨进程通信 2.1 Binder简介 2.2 Binder ...

  3. Android进阶——AIDL详解之使用远程服务AIDL实现进程间带远程回调接口和自定义Bean的较复杂通信小结(二)

    文章大纲 引言 一.远程回调AIDL接口的应用 1.封装基本的父类和一些工具类 2. 创建服务端的AIDL 2.1.定义回调AIDL接口 2.2.定义业务AIDL接口 3.实现服务端对应AIDL的带有 ...

  4. Android四大组件Service之AIDL详解

    Android四大组件Service之AIDL详解 前言 简介 基础知识 AIDL 服务端 定义AIDL文件规则 创建 .aidl 文件 清单注册 通过 IPC 传递对象 调用 IPC 方法 Andr ...

  5. 【朝花夕拾】Android跨进程通信总结篇

    前言 原文:https://www.cnblogs.com/andy-songwei/p/10256379.html 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一. ...

  6. 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇

    前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10256379.html],谢谢! 只要是面试高级工程师岗位,Android跨进程通信就是最受面 ...

  7. 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇...

    前言 原文:https://www.cnblogs.com/andy-songwei/p/10256379.html 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一. ...

  8. IPCInvoker,Android跨进程调用如此简单

    一个APP为什么需要多条进程? 如果一条进程能够拥有足够多的资源,且不会被系统kill掉的话,让程序运行在一条进程上是最好的选择.但是系统资源是按进程来分配的,每条进程资源分配是有个上限的,而且当我们 ...

  9. Android 跨进程通信基础

    2019独角兽企业重金招聘Python工程师标准>>> Android跨进程通信基础--Binder, BinderProxy, parcel, parcelable, Stub, ...

  10. Android 跨进程通信大总结

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/111553746 本文出自[赵彦军的博客] 文章目录 1.Android进程 2.修 ...

最新文章

  1. 动态指定超链接参数的几种方法(Passing a JavaScript variable into href of )
  2. lte 中crs_LTE网络CRS功率配置及其影响研究
  3. 浅谈equals和==的区别
  4. 吴恩达给 74 岁老父亲发证了!8 年完成 146 门课程!
  5. 使用备用访问映射改变站点访问路径
  6. PostgreSQL 统计信息pg_statistic格式及导入导出dump_stat - 兼容Oracle
  7. vue render函数_Vue原理解析(一):Vue到底是什么?
  8. sql azure 语法_如何:Azure中SQL Server文件快照备份
  9. 在K3凭证处理中的部份实用操作
  10. 使用cmake和vs2019进行编译libtorch过程
  11. 让APP不被android系统杀掉
  12. 02 - Tomcat配置
  13. python如何设置双索引_python-在新的多索引下串联熊猫列
  14. 鸿蒙 悟空遥控,利用悟空遥控推送软件,成功实现高德地图等三方APP装
  15. 新版眼保健操图解(转)
  16. 闰年c语言循环计算方法,C语言计算有多少闰年(答案原创)
  17. android组件化管理单例,Pigeon——支持增量编译和组件化开发的路由框架
  18. 使用rsync来实现文件同步
  19. 【Origin】Origin准确标注某点
  20. variant 类型

热门文章

  1. Javascript:js借助jQuery和fileSave将表格存储到world
  2. Java编程:获取输入的三种方法
  3. Cesium:鼠标监听事件绑定
  4. 诚毅学院的计算机专业,诚毅学院计算机专业JAVA题目
  5. ubuntu opencv多版本控制
  6. AGV机器人(1)基于视觉避障的理论基础
  7. 细粒度图像识别算法Mask-CNN
  8. 关于Junit中Assert已经过时
  9. I学霸官方免费教程四十二 :Java流之字节流 输入流和输出流 InputStream和OutputStream...
  10. 字符串模式匹配sunday算法