AIDL进程间调用与Binder的简单介绍

时间:2023-03-09 19:25:06
AIDL进程间调用与Binder的简单介绍

Binder是安卓中特有的一种进程间通信(IPC)方式,从Unix发展而来的手段,通信双方必须处理线程同步、内存管理等复杂问题,传统的Socket、匿名通道(Pipe)、匿名管道(FIFO)、信号量(Semaphore)、消息队列等,这些都从Android中去掉了。Socket是一种比较成熟的通信手段,同步控制也很容易实现,但是用于进程间通信,效率不高。

Android是整个系统运行的中枢,因此,Android在提高Binder的效率方面也下足了功夫。Android在进程间传递数据使用的是共享内存的方式,这样数据只需要复制一次就能从一个进程到达另一个进程(一般的IPC都需要两部,从用户进程复制到内核,再从内核复制到服务进程),这样数据传输的效率就大大提高了。
Binder调用时会传递调用进程的euid到服务端,因此服务端可以通过检查调用进程的权限来决定是否允许其使用所调用的服务。

定义一个AIDL接口
你必须使用java语法来写一个.aidl文件来定义你的AIDL接口,保存它在应用程序持有服务和应用程序绑定到服务两种情况的源代码中(src/目录下)。
当你编译每个包含.aidl文件的应用程序时,AndroidSDK工具会在.aidl文件的基础上生成一个IBinder接口并且保存它在工程的gen/目录下。service服务必须适当地实现IBinder接口。客户应用程序应该绑定服务并且调用IBinder下的方法去执行IPC。
为了创造一个使用AIDL的bounded service绑定服务,有下列步骤。
1.创造一个.aidl文件
这个文件定义了带方法签名的程序接口。
2.实现接口
AndroidSDK工具生成了一个java语言的接口,基于你的.aidl文件。
这个接口有一个内部的抽象类叫Stub继承Binder并且实现你的AIDL接口中的方法。
你必须继承Stub类并且实现方法
3.暴露接口给客户
实现service和重写onBind()方法去返回你的Stub类的实现。

1.创建AIDL文件
AIDL使用一个简单的语法让你声明可以传参并有返回值的一个或多个方法。参数和返回值可以是任何类型,甚至是其它AIDL生成的接口。
你必须使用java程序语言构造.aidl文件。每个.aidl文件必须定义一个简单的接口,要求有接口声明和方法签名。
一般地,AIDL支持下列数据类型:
》所有的java程序语言的基本类型(像int,long,char,boolean等等)
》String
》CharSequence
》List
所有的List里面的元素必须是这个列表里面支持的数据类型或者其它AIDL生成的接口或者你已经声明的序列化。一个List可能选择被用来作为泛型类(例如,List)。实际的具体另一方面负责接口的总是ArrayList,虽然这个方法通常使用List接口。
》Map
同上。
你必须包含进import语句给每个不在上面列出来的额外类型,即使它们是定义在和你的接口一样的包里面。
当定义你的服务接口时,要意识到:
》方法可以传(take)0个或者多个参数,可以返回一个值或者void
》所有的非基本类型要求一个方向性的标签暗示数据走的哪条路。是in,out或者inout.基本的默认的是in,不会是其它的。
》所有的代码注释包含在.aidl文件中,在生成的IBinder接口中(除了在导入和包声明语句)
》仅仅支持方法;你不能在AIDL中expose暴露static fields静态域

简单地保存你的.aidl文件到你的工程src/目录下,并且当你编译你的程序时,SDK工具会在你的工程的gen/目录下生成IBinder接口文件。生成文件名字匹配.aidl文件名字,但是带有一个.java后缀名(例如,IRemoteService.aidl会生成IRemoteService.java)

如果你使用AndroidStudio,递增地编译会让binder类立即生成。如果你没有使用AS,那么Gradle工具在下次你编译你的应用程序的时候会是生成binder类,你应该用gradle
assembleDebug(或者gradle
assembleRelease),一旦你完成.aidl文件的编写,所以你的代码可以链接到生成的类。

2.实现接口
当你编译应用程序时,Android SDK工具会在你的.aidl文件后生成一个.java接口文件。生成的接口包含了一个子类Stub(例如,YourInterface.Stub),这是一个抽象的父亲接口的实现并且声明了所有.aidl文件里面的方法。
Stub也定义了一些少量的帮助方法,必须注意到asInterface(),这会传递一个IBinder(通常将会这个传递到客户端的onServiceConnected()回调方法)并且返回一个stub接口的实例。
可以去看Calling anIPC Method寻求更多的关于怎么做这个转换的介绍。

为了实现这个从.aidl生成的接口,集成生成的Binder接口(例如,YourInterface.Stub)并且实现从.aidl文件继承的方法。
这里是一个叫IRemoteService接口实现的例子(由IRemoteService.aidl文件定义),使用一个匿名的实例。
现在mBinder是一个Stub类的实例(aBinder),这定义了服务的RPC接口。
在下一步,这个实例是要暴露给客户,这样它们能和service交互。
这里是一些当你实现你的AIDL接口应该意识的规则:
》来电不保证会在主线程执行,所以你需要从一开始就考虑多线程并且合适地构建你的service服务做到线程安全。
》默认地,RPC调用是异步地。
如果你知道service会花去一些毫秒数去完成请求,你不应该从activity活动的主线程调用它,因为它可能hang暂停应用程序(Android可能会展示ANR对话框)-你应该开一个线程去调用它。
》异常要抛出去给调用着。

3.暴露接口给客户端
一旦你已经实现了给你的service的接口,你就需要暴露它给客户端这样他们能绑定它。
为了暴露接口给你的service。继承Service并且实现onBind()方法然后返回一个你实现的生成的Stub类的实例(就像之前讨论的)。
现在,当客户(比如一个Activity)调用bindService()去连接服务的时候,客户端的onServiceConnected()回调方法会收到mBinder实例,这由service's服务的onBind方法返回。
客户必须可以访问接口类,所以如果客户和service在不同的应用程序间,那么客户的应用程序必须在src/目录下有一个.aidl文件的复制(这会生成android.os.Binder接口-提供给客户访问AIDL方法)。
当客户在onServiceConnected回调方法中收到IBinder,这必须调用YourServiceInterface.Stub.asInterface(service)去转换成返回类型到YourServiceInterface类型。

当在eclipse中写了.aidl文件时会自动编译出Java文件,

android studio中用aidl文件生成对应的java文件,先在main目录下新建aidl文件夹,
然后再新建一个包名,这个包名要和manifest文件里面的包名匹配,再写一个aidl
文件,注意写package,然后build一下就可以在app/build/generated/source/aidl目录下找到一个debug(可能是release)目录,就可以看到我们生成的java文件了。
注意不用拷贝到src目录下,如果拷贝,AS会提示重复。

main目录下新建aidl文件夹,再随便new出一个包名,写对应的aidl文件,然后可以在build.gradle下面的android范围内,写sourceSets {
main {
aidl.srcDirs = ['src/main/aidl']
}
}
再对工程build一下也可以解决。