1、概述
AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。是的,首先我们知道的第一点就是:AIDL是一种语言。既然是一种语言,那么相应的就很自然的衍生出了一些问题:
- 为什么要设计出这么一门语言?
- 它有哪些语法?
- 我们应该如何使用它?
- 再深入一点,我们可以思考,我们是如何通过它来达到我们的目的的?
- 更深入一点,为什么要这么设计这门语言?会不会有更好的方式来实现我们的目的
2、为什么要设计这门语言?
设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。
每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。每个进程之间都你不知我,我不知你,就像是隔江相望的两座小岛一样,都在同一个世界里,但又各自有着自己的世界。而AIDL,就是两座小岛之间沟通的桥梁。相对于它们而言,我们就好像造物主一样,我们可以通过AIDL来制定一些规则,规定它们能进行哪些交流——比如,它们可以在我们制定的规则下传输一些特定规格的数据。
总之,通过这门语言,我们可以愉快的在一个进程访问另一个进程的数据,甚至调用它的一些方法,当然,只能是特定的方法。
但是,如果仅仅是要进行跨进程通信的话,其实我们还有其他的一些选择,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 占用的系统资源比较多,如果是频繁的跨进程通信的话显然是不可取的;Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行,在有些要求多进程的情况下不适用——这种时候就需要使用 AIDL 了。如果想要了解它们更详细的区别的话,可以去另一篇博文看看:Android中的Service:Binder,Messenger,AIDL(2)
3,它有哪些语法?
其实AIDL这门语言非常的简单,基本上它的语法和 Java 是一样的,只是在一些细微处有些许差别——毕竟它只是被创造出来简化Android程序员工作的,太复杂不好——所以在这里我就着重的说一下它和 Java 不一样的地方。主要有下面这些点:
文件类型:用AIDL书写的文件的后缀是 .aidl,而不是 .java。
数据类型:AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包。比如,现在我们编写了两个文件,一个叫做 Book.java ,另一个叫做 BookManager.aidl,它们都在 com.lypeer.aidldemo 包下 ,现在我们需要在 .aidl 文件里使用 Book 对象,那么我们就必须在 .aidl 文件里面写上 import com.aidldemo.Book; 哪怕 .java 文件和 .aidl 文件就在一个包下。
默认支持的数据类型包括:
- Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
- String 类型。
- CharSequence类型。
- List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。
- Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的
- 定向tag:这是一个极易被忽略的点——这里的“被忽略”指的不是大家都不知道,而是很少人会正确的使用它。在我的理解里,定向 tag 是这样的:AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。具体的分析大家可以移步我的另一篇博文:你真的理解AIDL中的in,out,inout么?
- 另外,Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in 。还有,请注意,请不要滥用定向 tag ,而是要根据需要选取合适的——要是不管三七二十一,全都一上来就用 inout ,等工程大了系统的开销就会大很多——因为排列整理参数的开销是很昂贵的。
- 两种AIDL文件:在我的理解里,所有的AIDL文件大致可以分为两类。一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“Android接口定义语言”。
- 注:所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用。
1、AIDL支持的简单数据类型
- Java基本类型,即int、long、char等;
- String;
- CharSequence;
- List
List中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。 - Map
Map中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。定义AIDL
2、创建ServiceAIDL
import com.testmodelconnectionapp.MClientAIDL;
import com.testmodelconnectionapp.MsgBean;
interface MServiceAIDL {
/**
* 接收来自客户端发来的信息
* 模拟延时 6秒返回信息
*/
String getClientInfo(int num1,int num2,in MsgBean msgBean);
/*
*接受客户端AIDL
*/
MsgBean getServerInfo(in MClientAIDL clientAIDL);
}
3、client
import com.testmodelconnectionapp.MsgBean;
interface MClientAIDL {
//接受服务端的消息
String getServerInfo (in MsgBean msgBean,String str);
}
4、声明自定义类型
//声明自定义类型
parcelable MsgBean;
4、activity绑定并通过aidl调用service
private ServiceConnection conn = new ServiceConnection() {
//绑定上服务的时候
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//拿到了远程的服务
mServiceAIDL = MServiceAIDL.Stub.asInterface(service);
MClientAIDL.Stub stub = new MClientAIDL.Stub() {
@Override
public String getServerInfo(MsgBean msgBean, String str) throws RemoteException {
return null;
}
};
try {
//向服务端 传递binder
mServiceAIDL.getServerInfo(stub);
} catch (RemoteException e) {
e.printStackTrace();
}
}
//断开服务
@Override
public void onServiceDisconnected(ComponentName name) {
//回收资源
mServiceAIDL = null;
}
};
5、server
private IBinder iBinder = new MServiceAIDL.Stub() {
@Override
public String getClientInfo(int num1, int num2, MsgBean msgBean) throws RemoteException {
Log.i(TAG, "接收到Client的信息: ");
msgBean.setMsg1("接收到Client的信息");
mClientAIDL.getServerInfo(msgBean,"msgg Form Server");
return String.valueOf(num1+num2);
}
@Override
public MsgBean getServerInfo(MClientAIDL clientAIDL) throws RemoteException {
//收到客户端的binder
mClientAIDL = clientAIDL;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
MsgBean msgBean = new MsgBean();
msgBean.setMsg1("我是来自Server的Msg1");
try {
mClientAIDL.getServerInfo(msgBean,"msgg Form Server");
} catch (RemoteException e) {
e.printStackTrace();
}
}
},1000);
return null;
}
};