上一篇的service涉及到进程通信问题,主要解决办法是通过 messenger来发送消息,这也是Google推荐的进程通信方式,比较简单易懂嘛~~,messenger底层也是通过binder来实现的,对于binder,这里就不做介绍了。但是如果允许不同应用的客户端用 IPC 方式访问服务、在服务中处理多线程就不太适合了,还是得乖乖用AIDL,AIDL这玩意,估计不少人都会有点陌生吧,接来下就通过跨应用对书本进行增删查,来讲解如何通过AIDL实现 跨进程通信。

一、定义AIDL接口

在开始设计 AIDL 接口之前,要注意 AIDL 接口的调用是直接函数调用。不应该假设发生调用的线程。视调用来自本地进程还是远程进程中的线程,实际情况会有所差异。具体而言:

  • 来自本地进程的调用在发起调用的同一线程内执行。如果该线程是主 UI 线程,则该线程继续在 AIDL 接口中执行。如果该线程是其他线程,则其便是在服务中执行代码的线程。因此,只有在本地线程访问服务时,我们才能完全控制哪些线程在服务中执行(但如果真是这种情况,根本不应该使用 AIDL,而是应该通过实现 Binder 类创建接口)。
  • 来自远程进程的调用分派自平台在自有进程内部维护的线程池。必须为来自未知线程的多次并发传入调用做好准备。换言之,AIDL 接口的实现必须是完全线程安全实现。
  • oneway 关键字用于修改远程调用的行为。使用该关键字时,远程调用不会阻塞;它只是发送事务数据并立即返回。接口的实现最终接收此调用时,是以正常远程调用形式将其作为来自Binder 线程池的常规调用进行接收。如果oneway 用于本地调用,则不会有任何影响,调用仍是同步调用。
我们一般会定义一个AIDL文件(当然,也可以通过实现binder类来自己创建接口,不过暂时没那必要)。首先创建两个应用client端和service端,其中client端主要用于查询,增加数据,service端则负责实现数据存储。
先看service端,我们在src\main目录下创建aidl 包,在aidl下再创建一个目录来存放接口,同时在java目录下创建相同的目录,总体目录如下:
我们先来看aidl路径下的IBookManager.aidl 接口,很简单,就定义一个接口供client调用
package com.example.aidl;
import com.example.aidl.Book;interface IBookManager {List<Book> getBookList();void addBook(in Book book);boolean removeBook(in Book book);
}

在AIDl文件中可以使用的数据类型有:

  • Java 编程语言中的所有原语类型(如 intlongcharboolean 等等)
  • String
  • CharSequence
  • List

    List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。可选择将List 用作“通用”类(例如,List<String>)。另一端实际接收的具体类始终是ArrayList,但生成的方法使用的是List 接口。

  • Map

    Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。不支持通用 Map(如Map<String,Integer> 形式的 Map)。另一端实际接收的具体类始终是HashMap,但生成的方法使用的是Map 接口。

我们注意到这里的接口方法比一般的Java接口多了一个“in” ,在AIDl文件中规定除了基本数据类型外,其他数据必须表上方向:in ,out或者inout。
1)in代表输入型参数(服务端将会收到客户端对象的完整数据,但是客户端对象不会因为服务端对传参的修改而发生变动
2)out代表输出型参数(服务端将会收到客户端对象,该对象不为空,但是它里面的字段为空,但是在服务端对该对象作任何修改之后客户端的传参对象都会同步改动
3)inout 代表输入输出型参数(服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动)。
AIDL接口只支持方法,不支持声明静态常量。
由于我们使用到book这个对象,因此,需要声明该对象的AIDL文件,并在AIDL接口里面手动导入该文件,该文件声明如下
package com.example.aidl;
parcelable Book;

转到Java目录下,在相同的包下创建一个book对象,并使其实现Parcelable接口(AIDL要求接口中的类必须要实现Parcelable接口,并在AIDL目录下声明该类为parcelable,如上所示)

public class Book implements Parcelable {public int bookId;public String bookName;public Book(int bookId, String bookName) {this.bookId = bookId;this.bookName = bookName ;}private Book(Parcel parcel){bookId = parcel.readInt();bookName = parcel.readString() ;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(bookId);dest.writeString(bookName);}public static final Creator<Book> CREATOR = new Creator<Book>(){@Overridepublic Book createFromParcel(Parcel parcel) {return new Book(parcel);}@Overridepublic Book[] newArray(int i) {return new Book[i];}};
}

关键来了!!(敲黑板)   准备工作做完后,我们就可以生成相应的binder类了,选择build->make project即可,gradle会自动帮我们创建一个IBookManager的Java接口,生成的接口包括一个名为 Stub 的子类,这个子类是其父接口(例如,IBookManager.Stub)的抽象实现,用于声明 .aidl 文件中的所有方法。

二、实现AIDL接口

我们创建一个BookManagerService 并继承service,创建一个binder对象
   private Binder binder = new IBookManager.Stub(){@Overridepublic List<Book> getBookList() throws RemoteException {return null;}@Overridepublic void addBook(Book book) throws RemoteException {}@Overridepublic boolean removeBook(Book book) throws RemoteException {return false ;}};

现在,binder 是 Stub 类的一个实例,用于定义服务的 RPC 接口。在下一步中,将向客户端公开该实例,以便客户端能与服务进行交互。

在实现 AIDL 接口时应注意遵守以下这几个规则:

  • 由于不能保证在主线程上执行传入调用,因此一开始就需要做好多线程处理准备,并将服务正确地编译为线程安全服务。
  • 默认情况下,RPC 调用是同步调用。如果明知服务完成请求的时间不止几毫秒,就不应该从Activity的主线程调用服务,因为这样做可能会使应用挂起(Android可能会显示“Application is Not Responding”对话框)— 我们通常应该从客户端内的单独线程调用服务。
  • 在跨进程通信引发的任何异常都不会回传给调用方。

三、向client端公开该接口

我们通过重写onBind 方法来返回该bind接口
   @Overridepublic IBinder onBind(Intent intent) {Log.i(TAG, "onBind: ");return binder;}

现在,当客户端(如 Activity)调用 bindService() 以连接此服务时,客户端的 onServiceConnected() 回调会接收服务的 onBind() 方法返回的 binder实例。
最后我们在配置文件中声明该service,并设置其可以被其他应用调用

        <service android:name="com.example.aidl.BookManagerService"android:enabled="true"android:exported="true" />

至此,service端的准备工作就基本完成了,再来看看client端的

四、client端的实现

同service端一样,我们需要在client端创建一样的路径(因为底层采用对象的反序列化,路径必须一致),直接从service端copy一份过来即可,点击make project 创建对应的Java接口类,client端的准备工作就好了

我们来看 MainActivity,先是绑定service端的服务

   public void bindService(View view){Intent intent = new Intent() ;intent.setComponent(newComponentName("com.ujs.service","com.example.aidl.BookManagerService"));bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);}

这里我们用到intent.setComponent()方法,这是Android为了方便启动其他应用的组件而提供的,ComponentName有两个参数,第一个是目标应用的包名,第二个参数是目标组件的详细路径,然后我们传入一个serviceconnection,在onServiceConnected方法中获取service端的接口,这样我们就可以愉快的通过这个接口与service通信了

   private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.i(TAG, "onServiceConnected: -  ");iBookManager = IBookManager.Stub.asInterface(service);try {List<Book> list = iBookManager.getBookList();for(Book b:list){Log.i(TAG, "onServiceConnected: "+b.bookName);Log.i(TAG, "onServiceConnected: "+b.bookId);}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.i(TAG, "onServiceDisconnected: ");}};

我们可以通知service添加一本书

   public void addBook(View view){try {Book web = new Book(1,"web App");iBookManager.addBook(web);Log.i(TAG, "addBook: "+web.toString());Log.i(TAG, "addBook: 添加数据了");} catch (RemoteException e) {e.printStackTrace();}}

从service端获取所有书本信息

    public void upDate(View view){try {List<Book> list = iBookManager.getBookList();for(Book book : list){Log.i(TAG, "\nbook name : "+book.bookName+" book id :"+book.bookId);}} catch (RemoteException e) {e.printStackTrace();}}

通知service端删除一本书,并获取返回值

    public void remove(View view){try {boolean b = iBookManager.removeBook(web);if(b)Log.i(TAG, "remove: service 删除数据了");elseLog.i(TAG, "remove: 删除失败了");} catch (RemoteException e) {e.printStackTrace();}}

至此,基于AIDL进程通信就打通了,掌握这些就满足基本的进程通信需求了~~

附上源码

大话android 进程通信之AIDL相关推荐

  1. Android跨进程通信二——AIDL

    AIDL全称Android Interface Definition Language即安卓接口定义语言.主要用于多进程通信.比Messenger,它具有支持多线程优势 注意事项: 为了线程安全考虑, ...

  2. android进程通信6,[Android]你不知道的Android进程化(6)--进程通信Andromeda框架

    近来看到爱奇艺发布了多进程的架构框架Andromeda.研究一下其多进程的通信方式. 具体github地址 通过此框架的初步分析 1.通过grade插件完善AndroidManifest.xml配置文 ...

  3. [Android]进程通信Andromeda框架

    作者:苍王 时间:2018.6.1 以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章. [Android]如何做一个崩溃率少于千分之三噶应用app--章节列表 组件化群1已 ...

  4. [Android]你不知道的Android进程化(4)--进程通信AIDL框架

    Google爸爸,听说要将一些插件化hook系统的变量属性禁用,Android P之后很可能将会不再有插件化.热更新.主题变换.资源加固等骚操作.试图hook,你将会看到 NoSuchFieldExc ...

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

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

  6. Android 进阶8:进程通信之 Binder 机制浅析

    读完本文你将了解: IBinder Binder Binder 通信机制 Binder 驱动 Service Manager Binder 机制跨进程通信流程 Binder 机制的优点 总结 Than ...

  7. Android之解决aidl里面函数参数太大不能跨进程通信的问题

    问题: 因为做的截屏拍照是跨进程通信的,所以需要用aidl,但是参数传递用的byte[] image,网上查了下aidl传递的基本参数类型,没有byte[], 然后把图片转化成String,结果时候接 ...

  8. Android笔记 使用AIDL和远程服务实现进程通信

    简介 AIDL(Android Interface Definition Language:接口定义语言) 在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实 ...

  9. Android R版本 MtkSettings和Telephony进程通信aidl

    需要获取simunlockstate状态,而这个不由我们这边控制,在免于提Mtk case的情况下,通过Telephony获取这个值,telephony是个服务apk,不能通过android.bp引入 ...

最新文章

  1. 绘制电路图风格的纠结
  2. Android提高十六篇之使用NDK把彩图转换灰度图
  3. 深度探索C++ 对象模型(3)-默认构造函数Default Constructor
  4. docker 修改容器的主机名
  5. select隐藏_数仓|几种SQL隐藏的错误,你遇到过吗?
  6. Storyboard的简单使用
  7. GCD Game HDU - 7061
  8. struts2异常处理流程_Struts2异常处理示例教程
  9. 13.TCP/IP 详解卷1 --- IGMP : Internet 管理组协议
  10. redis的其他功能
  11. 基于SSM的旅游管理系统
  12. 国际国内资管分仓跟单软件的具体作用
  13. 【LeetCode】马三来刷题之 Single Number
  14. 抽象代数 04.01 群的生成元组
  15. ASUS AC88U 路由器开机自启方法
  16. jQuery菜鸟教程02
  17. 有一间计算机实验室英语怎么说,实验室英语怎么说
  18. GRBL二:串口控制命令及代码解析(转载)
  19. java 药店管理系统_java药店管理平台
  20. 不知道视频如何裁剪画面大小不变?来看看这篇文章

热门文章

  1. oracle之分析函数 开窗函数,Oracle之分析函数、开窗函数
  2. uipath sequence传递参数_多孔材料测试及声学参数识别(中)_多孔材料声学参数正向识别...
  3. mysql 同步 存储过程_mysql 存储过程 实现数据同步
  4. 深度学习 英文 训练阶段_半监督深度学习训练和实现小Tricks
  5. linux 子系统 巡检,Linux 系统巡检
  6. mysql 安装更改目录权限设置密码_mysql-8.0 安装教程(自定义配置文件,密码方式已修改)...
  7. gitee使用svn_Gitee SVN支持
  8. 11、计算机图形学——几何(贝塞尔曲线与曲面)
  9. yolov5检测完不显示框和标注
  10. python中pygal_Python数据可视化之Pygal图表类型