AIDL进程间通信之AIDL
转载请标明出处:http://blog.****.net/u013254166/article/details/78612405
本文出自: 【rhino博客】
本篇主要讲解Android进程间通信手段——AIDL,即AndroidInterface Definition Language(Android接口定义语言)。文章没有粘贴太多的代码,最后面有源码的下载链接。
1. 简述
为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。
要进行跨进程通信的话,其实我们还有其他的一些选择,比如BroadcastReceiver , Messenger 等。但是 BroadcastReceiver 占用的系统资源比较多,如果是频繁的跨进程通信的话显然是不可取的;Messenger进行跨进程通信时请求队列是同步进行的,无法并发执行,在多进程的情况下不适用。这种时候就需要使用AIDL 了。
官方文档特别提醒我们何时使用AIDL是必要的:“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL,…”。
2. 语法
其实AIDL这门语言非常的简单,基本上它的语法和Java 一样,只是在一些细微处有些许差别。
文件类型:用AIDL书写的文件的后缀是.aidl,而不是.java。
数据类型:AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的。但是除了这些类型之外的数据类型,在使用之前必须导包,就算引用目标文件与当前正在编写的.aidl 文件在同一个包下(在 Java 中不需要)。默认支持的数据类型包括:
Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
String 类型。
CharSequence类型。
List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。List可以使用泛型。
Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。
定向tag:表示了在跨进程通信中数据的流向,用于修饰参数。
in 表示数据只能由客户端流向服务端,表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动。
out 表示数据只能由服务端流向客户端,表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动。
inout 则表示数据可在服务端与客户端之间双向流通,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。(与java一样)
两类AIDL文件:
第一类:用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型。
第二类:用来定义方法接口,以供系统使用来完成跨进程通信的。
注:所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用。
3. 使用实例
3.1 新建AIDL文件
右键module名>New>AIDL>AIDLFile。
新建aidl文件后,比起以前多了一个叫做aidl 的包,而且他的层级是和java 包相同的,并且aidl 包里默认有着和java 包里默认的包结构。如果AS版本较低,可能不会自动生成,可以手动新建包结构。
Person.aidl定义前面说到的第一类文件,由parcelable修饰,供PersonManager引用。
PersonManager.aidl定义前面说到的第二类文件,主要是定义接口。
Person.java定义Person.aidl的具体内容,需要实现Parcelable接口。默认生成的模板类的对象只支持为in的定向 tag。因为默认生成的类里面只有 writeToParcel() 方法,而如果要支持为out 或者inout 的定向 tag 的话,还需要实现readFromParcel() 方法,(这个方法Parcelable 接口没有)。
3.2 找不到文件
Gradle默认是将java 代码的访问路径设置在java 包下的,这样一来,如果java 文件是放在aidl 包下的话那么理所当然系统是找不到这个java 文件的。那应该怎么办呢?
a. 修改build.gradle 文件:在 android{} 中间加上下面的内容:
sourceSets {
main {
java.srcDirs= ['src/main/java', 'src/main/aidl']
}
}
b. 把 java 文件放到java 包下去:把 Person.java 放到 java 包里任意一个包下,保持其包名不变,与 Person.aidl 一致。只要它的包名不变,Person.aidl就能找到 Person.java,而只要 Person.java 在java 包下,那么系统也是能找到它的。但是这样做的话也有一个问题,就是在移植相关.aidl 文件和 .java 文件的时候没那么方便,不能直接把整个aidl 文件夹拿过去完事儿了,还要单独将.java 文件放到 java 文件夹里去。
3.3 移植aidl相关文件
在客户端和服务端中都有我们需要用到的.aidl文件和其中涉及到的.java文件。因此不管在哪一端写的这些东西,写完之后我们都要把这些文件复制到另一端去。
当然如果是在同一app就不需要移植了。
3.4 编写服务端代码 AIDLService.java
在服务端实现AIDL中定义的方法接口的具体逻辑,在onBind回调中返回该接口mPersonManager,然后客户端bind该服务器的时候,通过回调能拿到mPersonManager实例,从而可以通过mPersonManager实例调用通信接口,从而达到跨进程通信的目的。
privatefinal PersonManager.Stub mPersonManager = new PersonManager.Stub(){
//这里实现PersonManager的接口
}
……
public IBinderonBind(Intent intent) {
return mPersonManager;
}
AndroidManifest.xml
<serviceandroid:name=".AIDLService">
<intent-filter>
<action android:name="com.rhino.aidl.test" />
</intent-filter>
</service>
3.5 编写客户端代码 MainActivity.java
privateServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 将服务端的Binder对象转换为客户端所需要的接口对象,
// 该过程区分进程,如果进程一样,就返回服务端Stub对象本身,
// 否则呢就返回封装后的Stub.Proxy对象。
mPersonManager = PersonManager.Stub.asInterface(service);
mIsBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIsBound = false;
}
};
// 启动服务
privatevoid attemptToBindService() {
Intent intent = new Intent();
intent.setAction("com.rhino.aidl.test");
intent.setPackage("com.rhino.aidlserver");
bindService(intent, mServiceConnection,Context.BIND_AUTO_CREATE);
}