AIDL —— Android接口定义语言

时间:2023-03-10 06:37:53
AIDL —— Android接口定义语言

AIDL:Android Interface Definition Language,即Android接口定义语言,是Android进程间通信比较常用的一种方式。翻译一下,就是为了让某个Service与多个应用程序组件之间进行跨进程通信,从而实现多个应用程序共享同一个Service,以此来实现不同APP间的交互。

可以对上面这段话进行一些拆解。

什么是接口定义语言

IDL,接口定义语言(Interface Definition Language),也叫接口描述语言(Interface Description Language),是一个描述软件组件接口的语言规范。通过IDL文件定义公共的应用程序接口(API),再由服务程序中的对象向外公布。

From wiki:

An interface description language or interface definition language (IDL), is a specification language used to describe a software component's application programming interface (API).

什么是进程间通信

进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。简单来说,进程间通信就是指进程间数据交互的过程,是一个进程与另一个进程间共享消息的一种通信方式。

消息(message)是发送进程形成的一个消息块,将消息内容传送给接收进程。IPC机制是消息从一个进程的地址空间拷贝到另一个进程的地址空间。

补充说明:

按照操作系统中的描述,进程一般指一个执行单元,是系统进行资源分配和调度的基本单位,是操作系统结构的基础,在PC和移动设备上指一个程序或者一个应用;一个应用至少有一个进程(当然也可以拥有多个进程),一个进程中可以包含了多个线程,但至少要包含一个线程(线程是CPU执行的最小调度单元,也是一种有限的系统资源)。进程相当于是线程的容器,线程可以与进程内的其他线程一起共享进程的资源。

在android中,主线程也叫UI线程,在UI线程里才能操作界面元素。很多时候,一个进程中需要执行大量的、耗时的任务。如果这些任务放在主线程中去执行就会造成界面无法响应,也就是我们常说的“ANR”(Application Not Responding)。

进程间通信的原因

在Linux系统中,虚拟内存机制为每个进程分配了线性连续的内存空间,操作系统将这种虚拟内存空间映射到物理内存空间,每个进程有自己的虚拟内存空间。因此每个进程只能操作自己的虚拟内存空间,而不能操作其他进程的内存空间,只有操作系统才有权限操作物理内存空间,这就是进程隔离。进程隔离保证了每个进程的内存安全,但是在大多数情形下,不同进程间的数据通讯是不可避免的,因此操作系统必须提供跨进程通信机制。

进程间通信的目的

数据传输:一个进程需要将它的数据发送给另一进程,发送的数据量在一个字节到几兆字节之间。

共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。

事件通知:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

资源共享:多个进程之间共享同样的资源。为了做到这一点,需要内核提供锁和同步机制。

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变

进程间通信的原理

AIDL —— Android接口定义语言

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核。在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信机制(IPC机制)。

主要的过程如下图所示:

AIDL —— Android接口定义语言

IPC机制是现代操作系统中都存在的机制,而对于Android来说,它虽然是基于Linux内核的移动操作系统,但它的进程间通信并不能完全继承于Linux。因此,为了使其他的应用程序也能够访问本应用程序提供的服务,Android系统采用了远程过程调用(RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。

补充:IDL通常用于RPC软件,它提供了一个“桥梁”用来连接不同的系统。

 

什么是RPC

RPC(Remote Procedure Call)即远程过程调用,它是一种通过网络从远程计算机程序上请求服务,在不需要了解底层网络技术的协议下,即可获取计算机进程中的数据。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC在OSI网络通信7层模型中位于会话层:

AIDL —— Android接口定义语言

RPC与IPC的关系

Android 利用远程过程调用 (RPC) 提供了一种进程间通信 (IPC) 机制,通过这种机制,由 Activity 或其他应用组件调用的方法可(在其他进程中)远程执行,而所有结果将返回给调用方。

这就要求把方法调用及其数据分解至操作系统可以识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。 然后,返回值将沿相反方向传输回来。 Android 提供了执行这些 IPC 事务所需的全部代码,因此只需进行定义和实现 RPC 编程接口即可。

也就是说,RPC在Android的具体体现是依赖 bindService() 的方式,通过 onBind 方法将服务端的计算结果返回给客户端(Activity等组件)的过程。

备注:要执行 IPC,必须使用 bindService() 将应用绑定到服务上。

既然说到了Android应用程序的组件,那这里也简单提一句:Android系统中4种应用程序组件分别为——Activity、Content Provider、Broadcast Receiver和Service。

Android SDK中提供的四种跨进程通信的方式正好对应于上面的四种组件:

① Activity可以跨进程调用其他应用程序的Activity;

② Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),也可以对其他应用程序的数据进行增、删、改、查操作;

③ Broadcast可以向android系统中所有应用程序发送广播,需要跨进程通讯的应用程序可以监听这些广播;

④ Service和Content Provider类似,也可以访问其他应用程序中的数据,但不同的是:ContentProvider返回的是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。

可以用一张图来表示:

AIDL —— Android接口定义语言

补充:

① 广播有一个缺点,在手机中广播较多时会有明显的延时,甚至有广播发送不成功的情况出现,因此利用AIDL Servic实现跨进程通信应运而生,虽然实现上稍微麻烦点儿。

② 注意普通的Service并不能实现跨进程操作,实际上普通的Service和它所在的应用处于同一个进程中,而它也不会专门开一条新的线程,因此如果在普通的Service中实现耗时的任务,需要新开线程;因此,要实现跨进程通信,需要借助AIDL。Android中的跨进程服务其实是采用C/S的架构,因而AIDL的目的就是实现通信接口。

③ 也就是说,AIDL其实就是Android中众多进程间通信方式中的其中一种方式。

那么,具体实现进程间通信的方式有什么呢?下面就介绍下几种常用方式:

1. Bundle(组件间)

我们知道,四大组件中的三大组件(Activity,Service,Receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现Parcelable接口,所以它可以方便地在不同的进程中传输。基于这一点,当我们在一个进程中启动了另一个进程的Activity、Service、Receiver,就可以在Bundle中附加我们需要传输给远程进程的信息并通过Intent发送出去。当然,我们传输的数据必须被序列化,比如基本数据类型、实现了Parcelable的对象,实现了Serializable的对象以及一些android支持的特殊对象,具体内容可以看Bundle这个类,就可以看到所有它支持的类型。Bundle不支持的类型我们无法通过进程间传递数据。

2. 文件共享

共享文件也是一种不错的进程间的通信方式。两个进程通过读/写同一个文件来交换数据。比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。我们在windows上,一个文件如果被加了排斥锁将会导致其他线程无法对其进行访问,包括读和写。不过因为android系统基于Linux,使其并发读/写可以没有任何限制的进行,甚至可以两个线程同时对一个文件进行写操作(虽然这可能会出问题)。通过文件交换数据很好使用,除了可以交换一些文本信息外,我们还可以在序列化一个对象到文件系统的同时,从另一个进程中恢复这个对象。反序列化得到的对象只是在内容上和序列化的对象是一样的,但它们本质上是两个对象。

当然,SharePreferences是个特例。SharePreferences是Android中提供的轻量级的存储方案,它通过键值对的方式来存储数据,在底层实现上它采用xml文件来存储键值对,每个应用的SharePreferences文件都可以在当前所在的data目录下查看到。从本质上来说,SharePreferences也属于文件的缓存,因此在多进程模式下,系统对它的读/写就变的不可靠,当面对高并发的模式读/写访问,SharePrefenerces有很大几率会丢失数据,因此,不建议在进程通信中使用SharePreferences。

3. Messenger(基于Binder)

Messager翻译为信使,顾名思义,通过它可以在不同进程间传递Message对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据进程间传递了。Messenger是一种轻量级的IPC,它的底层实现是AIDL,从其两个构造方法中就可以明显看出AIDL的痕迹,不管是I Messenger还是Stub.asInterface,这种使用方法都表明它的底层是AIDL。

public Messager(Handler target){
mTarget = target.getIMessenger();
} public Messenger(IBinder target){
mTarget = IMessenger.Stub.asInterface(target);
}

Messenger的使用方法很简单,它对AIDL做了封装,使得我们可以更简单地进行进程间通信。同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为在服务端中不存在并发执行的情形。实现一个Messager有如下步骤,分为客户端和服务端:

服务端进程

首先,我们需要在服务端创建一个Service来处理客户端的链接请求,同时创建一个Handler ,并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。

客户端进程

客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端

4. AIDL(基于Binder)

Messager是以串行的方式来进行处理客户端发来的消息,如果大量的消息同时发送到服务器,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger显然就不太合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形下Messager就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。AIDL也是Messenger的底层实现,因此Messenger本质上也是AIDL,只不过系统为我们做了封装,从而方便上层的调用而已。

服务端

服务端首先要创建一个Service用来监听客户端的链接请求,然后创建一个AIDL文件,将暴露给客户的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。

客户端

客户端所做的事情就要稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。

AIDL接口的创建

首先先看AIDL接口的创建,我们创建了一个后缀名为AIDL的文件,在里面声明了一个接口和两个接口的方法

AIDL —— Android接口定义语言

在AIDL中并不是所有的数据都能使用的,AIDL支持以下数据类型的数据传输:

Java的8种基本数据类型:int、long、char、boolean、double等

String和CharSequence

List:接收方必须是ArrayList,里面的每个元素都必须能够被AIDL支持

Map:接收方必须是HashMap,里面的每个元素都必须被AIDL支持

Parcelable:所有Parcelable接口的实现类,本质上是序列化与反序列化的过程

AIDL接口:所有AIDL接口本身也可作为可支持的数据类型,传递的是引用

需要注意的是:如果AIDL文件中使用到了自定义Parcelable对象和AIDL接口定义的对象,则必须要在所引用的AIDL文件中显式import进来,不管它们是否和当前的AIDL文件位于同一个包内。

另一个需要注意的地方是:如果AIDL文件中用到自定义的Parcelable对象,则必须创建一个与它同名的AIDL文件,并在其中声明该对象为Parcelable类型。

5. Content Provider(基于Binder)

ContentProvider是android中专门用于不同应用间进行数据共享的方式。和Messager一样,底层同样使用了Binder,由此可见,Binder在Android系统中是何等重要。

系统预制了很多ContentProvider,比如通讯录信息、日程表信息等,要跨进程访问这些信息,只需要通过ContentResolver的query、update、insert和delete方法即可。

6. Socket(网络)

Socket位于传输层,是网络上的两个程序通过一个双向的通信连接实现数据交换的一种进程通信方式。

socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供网络开发所用的接口;HTTP是轿车,提供了封装或显示数据的具体形式;socket是发动机,提供了网络通信的能力。

socket的使用是基于TCP或UDP协议。TCP面向连接,可靠稳定,双向,有着经典的3次挥手、4次挥手,有丢包重发机制;UDP面向地址,不稳定,双向,拥塞、复杂网络环境可能会丢失,但效率更高。

补充:TCP用于在传输层有必要实现可靠传输的情况,由于它是面向连接并具有“顺序控制”、重发控制等机制;而UDP则主要用于那些对高速传输和实时性有较高要求的通信或广播通信。

因此TCP和UDP应该根据应用的目的按需使用,没有绝对的优缺点。

最后引用一下Android开发艺术探索的总结,很全面啦~

AIDL —— Android接口定义语言

通过对Android实现进程间通信方式的介绍,可以看出Binder的出镜率很高,其中AIDL、Messager、ContentProvider都是基于Binder来实现的。可以说,Binder是Android最主要、也是最有特色的进程间通信的方式。

Binder是什么

直观来说,Binder是android中的一个类,它实现了IBinder接口。

从IPC角度来看,Binder是android中一种跨进程通讯的方式。Binder可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通讯方式在Linux中没有。

从内存访问的角度来看:Binder的作用就是让两个进程可以访问同一块内存空间,实现数据交换。

从Android应用层来看:Binder是客户端和服务端进行通信的媒介。服务端会返回一个包含了服务端业务调用的Binder对象,然后客户端通过这个Binder对象就可以获取服务端提供的服务或数据。

从Android FrameWork角度来看:Binder是ServiceManager连接各种Manager和相应ManagerService的桥梁。

AIDL —— Android接口定义语言

Binder采用的是Client-Server结构,客户端进程通过获取服务端进程的代理,并向代理接口方法中读写数据来完成进程间的通信。它的传输过程只需要一次拷贝,并为发送添加UID/PID身份,安全性较高。

对于Server而言,Binder可以看成是Server实现特定服务访问的接入地址,Client通过访问这个地址实现对Server的请求;对于Client而言,Binder可以看成是通向Server的管道入口,client要想和某个server通信,就必须先建立管道和管道入口。

Binder原理

从组件视角来说,Binder的通信框架包含Client、Server、Service Manager和Binder驱动。他们的关系类似与互联网中:Server是服务器,Client是客户终端,ServiceManager是域名服务器(DNS),Binder驱动是路由器。

AIDL —— Android接口定义语言

说明:

① Service Manager用于管理系统中的各种服务

② Client、Server和Service Manager运行在用户空间,Binder驱动运行在内核空间

③ Binder驱动和Service Manager在Android平台中已经实现,可以看做是Android平台的基础架构,Client和Server是Android的应用层。开发人员只需自定义实现Client和Server,借助Android的基本平台架构便可以直接进行IPC通信

④ Binder驱动程序提供设备文件/dev/binder与用户空间交互,Client、Server和Service Manager通过open和ioctl文件操作函数与Binder驱动程序进行通信

⑤ Client和Server之间的进程间通信通过Binder驱动程序间接实现

⑥ Service Manager是整个Binder通信机制的大管家,是Android进程间通信机制Binder的守护进程,用来管理Server,并向Client提供查询Server的接口

备注:无论是注册服务和获取服务的过程都需要Service Manager,需要注意的是此处的Service Manager是指Native层的Service Manager(C++),并非指framework层的Service Manager(Java)。

Binder的工作机制图

AIDL —— Android接口定义语言

  当客户端发起远程请求时,当前线程会被挂起直到服务端进程返回数据

  如果一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求

  服务端的Binder方法是运行在Binder的线程池中的

  通过linkToDeath方法(对应unlinkToDeath)可以给Binder设置一个死亡代理,当Binder死亡时,能够收到通知进而重新发起连接请求,恢复连接

BinderAIDL之间的关系

① Binder 可以简单认为是 Android 特有的一种进程间通信方式,这种进程间通信方式的核心在于内核中的 Binder 驱动,该驱动负责数据的 IPC 双向传递,向 Client 端提供访问接口,并对 Server 端进行调用。

在 Android 中,Binder 通信也是可以支持传递“序列化”过的 Java 字节流的,但是它也只是将这串字节流当做普通数据类型来处理,它只负责传递,具体如何解释这些数据流,需要有双方约定,这个约定就是“序列化”和“反序列化”。

② AIDL 是为了使其他的应用程序也可以访问本应用程序提供的 Service(这里的 Service 并不是特指 Android 四大组件中的Service,而是 Service 端可以向 Client 端提供的调用),Android 系统采用了 RPC 方式来实现。Android 使用一种 IDL 来公开服务的接口,这种能够跨进程访问的服务就被成为 AIDL 服务。AIDL 就是 Android 中众多进程间通讯方式中的一种方式。

AIDL —— Android接口定义语言

因此,网上给出的总结为:AIDL其实就一种接口的声明规范,这里的接口指的是服务端提供的访问接口,按照该规范写出来的带有aidl后缀的接口文件就是AIDL文件,通过编译工具(aidl.exe)对AIDL文件进行编译后,生成了可通过Binder IPC进行传递的binder数据类型(即Stub,该数据类型扩展了Binder,并实现了上述接口),用户再通过扩展该Stub类,来定义服务端具体的、可供调用的功能。

参考文章

Android开发艺术探索

https://www.jianshu.com/p/fff0661ecbcd

https://www.jianshu.com/p/d1fac6ccee98

https://www.jianshu.com/p/8e4f9978e5b8

http://gityuan.com/2015/10/31/binder-prepare/

https://www.cnblogs.com/sqchen/p/10660939.html

https://blog.****.net/qq_35770354/article/details/83046437

https://blog.****.net/hzw2017/java/article/details/81275438

https://blog.****.net/Tomasyb/java/article/details/72877958

https://blog.****.net/ding3106/java/article/details/83506819

https://blog.****.net/mijiaxiaojiu/java/article/details/101348324

https://blog.****.net/qq_36046305/article/details/84769349