有过一定工作经验的Android程序员,肯定是听过AIDL的。之前我也是经常听别人讲,最近下功夫去专门看了下,总算自己参照示例写出来一个Demo出来,为了加深印象,总结本篇文章。
AIDL:Android Interface Definition Language,Android接口定义语言。看这名称:是一种语言。我学过java语言、C语言、C++语言,虽然知道每种语言之间有一定的互通之处,但是却也是各有各的规则。AIDL称为一种语言,想来应该有一套完整体系。在来关注 Interface ,接口,我们知道,接口是不干活的,只是定义方法返回值、方法名称、参数列表,有接口,那就一定要有实现,就是必须的有人是真正干活的。
再来说说跨进程通信,在Android系统中,每个进程都在自己的Dalivik中运行,进程之间不会干扰。但是有时进程之前需要通信,比如App要查询下当前系统的后台进程,就需要去调用AMS提供的可查询后台进程的方法。
跨进程通信有很很多种技术手段,比如我们常用的通过Intent去调起另外一个进程、发广播(发送者和接受者不在同一个进程中)。
我们今天来说说AIDL的实现步骤:
既然是跨进程,那就必须有两个进程:一个作为Server,一个作为Client。
我们实现一个client向server请求书籍信息,并向server添加一个书籍。
先说Server端:
在AS里面新建一个空白工程,既然是书请求籍信息,就必须要有个Book类。这里在补充一点,AIDL默认只支持特定的数据类型:
- Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
String 类型。 - CharSequence类型。
- List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。
- Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。
不是这些默认支持的数据类型,就需要创建一个parcelable的AIDL文件将其序列化。(序列化是为了保证数据能在进程之间传输,关于序列化的内容,读者可自行去了解)
那个就要创建一个Book的aidl文件。
在AS中选中app,然后右键new aidl,填入Book,就创建好了。可以将其中默认的实现删除,Book.aidl是为了做序列化的,其内容也很简单,只要两行。
创建了AIDL文件之后,工程结构如下:
在原本java的目录下有一个aidl的文件,并且包名也与java下一致。有了Book.aidl,还要创建Book.java,来描述Book类的成员变量,并且Book.java要实现Parcelable接口,Book.java内容如下,其只包含一个String类型的name属性。
Book.aidl创建好了,接下来就要创建一个供client调用的接口定义的AIDL,也就是BookController.aidl。
根据计划,Server打算提供一个获取书籍 和 添加书籍的接口方法。
好了,到这里Server端的接口写好了,难道就靠这个只有8行代码的AIDL文件就能跨进程通信了吗?
这就设计到AIDL的原理了,ADIL的被设计出来的目的是为了给程序员提供一种跨进程通信的方法,并且为了减少程序员的手动编写代码 和 保证AIDL的稳定,程序要只要编写了上述的接口,然后通过AS去clean下工程,在编译下,就会在build目录下自动生成BookController的实现类
任何跨进程通信的方法,都离不开Binder,Binder亦是Android系统的进程间通信基石。
这个自动生成的BookController就是基于Binder通信原理来的。也就是说,真正能在两个进程之间通信的通路已经铺好了。现在还需要在Server端去实现这两个接口。我们通过一个Service来实现。
如果你仔细看了上面build目录下生成的BookController,就知道这里的 BookController.Stub() 是什么了。
另外,Service需要在Manifest中配置一下:
<service android:name=".AIDLService"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="com.cyy.findAidlServer"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
好了,到这里Server端的代码都写完了,我们来看下Client端要如何调用Server提供的两个方法。
AIDL有一个要求,为了保证Server与Client能正常通信,需要使跨进程通信涉及到的java和aidl文件在两端的内容和包名保持一致。
所以,我们需要先将Server端的Book.java、Book.aidl、BookController.aidl这3个文件copy到Client端,并且保证在Client端的包名与服务器一致。
在AS中新建一个空白工程,移植后的效果工程目录结构如下:
移植完Server端的相关文件之后,我们在MainAcitivity中来调用Server调用Server的接口。
首先,需要绑定Server端的AIDL的Service,获取mBookController 对象。
private void attemptToConnectService(){
Intent intent = new Intent();
intent.setAction("com.cyy.findAidlServer");
intent.setPackage("com.cyy.server");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBookController = BookController.Stub.asInterface(service);
Log.e("cyy","Client 绑定Service成功");
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBookController = null;
}
};
绑定成功之后,我们在通过两个按钮的点击事件去分别调用Server段的两个接口。
分别安装Server、Client到手机上,然后运行下Server,使Server的Service被创建,在打开Client,点击Client上的两个按钮,通过log可以看到Server端的Service中的方法确实被调用了。
至此,我们就通过AIDL实现了两个进程之间的通信。
最后,补充下我在做这个Demo的过程中遇到的两个问题:
1,要先创建Book.aidl,再去new Book.aidl文件,否则会报文件已存在问题
2,我在创建了Book.aidl文件后,编译app,一直报 Book.aidl1.1-2: syntax error, unexpected unrecognized character, expecting annotation,但这个文件只有两行内容,不可能有什么错误的,一通百度后也没有结果。最后发现是公司的加密系统,把aidl文件加密了,导致编译不过,解密之后就OK了。
以上,关于AIDL的内容,希望能帮助到你。