初探AIDL

了解AIDL的使用方法


概要:

  1. 介绍一个AIDL的Demo
  2. 传递自定义类型

Demo介绍:
包含服务端和客户端,客户端通过远程绑定服务去调用服务端的方法,服务端再把数据返回给客户端,IDE使用的是Android Studio。

一、Demo

服务端

1、创建一个名为AIDLService的project作为服务端
2、创建aidl文件,定义接口

创建一个名为IMathAidlInterface的aidl文件,在Android Studio中直接创建aidl文件会自动帮我们创建AIDL文件夹以及package。

Android Studio自动为IMathAidlInterface文件填充的模板如下:

其中那个接口内的自动出现的方法是展示可以使用的基本类型,包括所有的基本数据类型(short除外)、String、CharSequence、List、Map、Parcelable

这里可以删去,定义我们自己计算接口,这里定义一个加法接口。

这里注意,接口名IMathAidlInterface要和aidl文件名要保持一致。

至此AIDL文件创建完毕,这里介绍一下AIDL文件的作用:

  • AIDL全称Android Interface Definition Language (AIDL),Android接口定义语言。利用这个文件就可以生成一个对应的Java文件。在Eclipse中,编写完AIDL文件,类似R文件,会自动在gen目录下生成一个AIDL对应的Java文件,但在Android Studio中,不会自动生成,需要我们手动”Make Projct”或者”Sync Project With Gradle Files”,才会在build/generated/source/aidl/debug/目录下生成AIDL对应的AIDL文件。如果没有IDE,我们也可以通过sdk build-tools内的aidl.exe程序在dos环境下利用aidl文件生成对应的java文件,命令为aidl file_name.aidl,其实,我们也还可以不用编写aidl文件来生成对应的java文件,可以自己手写这个java文件。下图为Android Studio为我们生成的文件:

点进度看IMathAidlInterface.java里面的内容:

可以看到IMathAidlInterface是一个接口,继承自android.os.IInterface接口,翻到最下面,就可以看到我们刚才在AIDL文件中定义的加法接口:

3、创建远程服务,供客户端远程调用

这里创建一个名为MathService的服务,这里刚才生成的java文件就派上用场了,定义一个IMathAidlInterface接口的实现类mStub,重写add方法,在Service的onBind方法中,return这个mStub,顺便在add方法内这里打印一下Log,方便等会测试。

然后就是注册我们的Service服务了:

注意这里的android:exported="true"不能少,否则等会客户端远程绑定服务时会出错,提示不允许绑定服务。

至此服务端创建完毕,下面创建客户端。

客户端

1、创建一个名为AIDLClient的project作为客户端
2、讲服务端project中的的AIDL文件夹拷贝到客户端project内(注意aidl文件夹的层级,和java文件夹在同一级)

然后手动”Make Projct”或者”Sync Project With Gradle Files”,也使其生成对应的IMathAidlInterface.java文件

3、客户端远程绑定服务端的Service

这里我们在客户端的onCreate方法内绑定服务,使客户端启动的时候就立即绑定。然后再onServiceConnected方法内,接收远程服务返回给我们的IMathAidlInterface接口实现对象,有了这个对象,我们就可以调用远程服务的方法了。

这里calculate为按钮的点击事件,计算5加6的和,toast弹出显示,最后,不要忘记在客户端的onDestroy方法内解绑ServiceConnection对象。

至此,客户端编写完毕,然后先部署服务端,再部署客户端运行查看效果:

显示的结果为,此时远程服务的add方法也调用,因为从打印的log可以看得出来:

至此,一个简单的AIDL Demo远程调用示例演示完毕。


二、传递自定义类型

传递自定义类型时,需要自定义的类需要序列化,比如实现Parcelable接口,这里我们将上面的例子改成向服务端一个List集合中不断的添加Person对象,然后服务端将集合返回给客户端

首选在服务端定义一个Person类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class Person implements Parcelable {

private String name;
private String age;

protected Person(Parcel in) {
name = in.readString();
age = in.readString();
}

public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}

@Override
public Person[] newArray(int size) {
return new Person[size];
}
};

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAge() {
return age;
}

public void setAge(String age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeString(age);
}
}

然后将服务端的aidl文件修改如下所示:

这时候,我们手动编译下,使其产生对应的java文件,这时候会出错,因为Person虽然实现了序列化,但是这里Person类在aidl中识别不了,我们需要在aidl目录内同时新建一个Person.aidl文件用来标识:

这样还不行,还需要在IMathAidlInterface.aidl文件内import这个Person,并且在参数内指定in,out,or inout,这里指定in,如下图所示:

这个时候再编译就没有问题了,然后把服务端的MathService调整修改一下,如下图所示:

服务端修改完毕,接着修改客户端。

首先把服务端的aidl文件夹copy覆盖到客户端,同时也要把客户端java文件夹内刚才实现Parcelable的Person类也要copy到客户端java文件夹内,注意需要保持Person在服务端的包名一致,所以在客户端需要手动创建一个和服务端一样的包名并把Person类放进去,然后把按钮点击事件内的代码删掉,然后编译客户端,应该不会出现什么问题。

然后编写客户端的添加逻辑,并打印服务端返回的集合,然后先部署服务端,再部署客户端并运行查看效果:

从log来看点击了三次按钮,效果和期望的一样,每次添加一个Person,然后并返回。

至此,成功的演示了利用AIDL实现自定义类型Person的跨进程传输。

下篇文章分析AIDL的原理实现。