盒子
盒子
文章目录
  1. aidl的简单用法
  2. aidl的原理
    1. 服务端通信原理
    2. 客户端原理
    3. aidl原理图
  3. 使用aidl传递复杂数据类型
    1. 输入参数和输出参数

Android温故而知新 - AIDL

这篇文章让我们一起来复习一下aidl

aidl的简单用法

aidl的用法是很简单的。首先创建IDemoAidlInterface.aidl文件(在服务端工程和客户端工程中需要分别定义一个相同的aidl文件):

1
2
3
4
5
package linjw.demo.aidldemo;

interface IDemoAidlInterface {
int add(int a, int b);
}

然后在service.onBind()中创建一个IDemoAidlInterface.Stub返回:

1
2
3
4
5
6
7
8
9
10
11
12
public class DemoService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IDemoAidlInterface.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
}
}

这样在bindService的时候就能获得一个IDemoAidlInterface,就可以通过它去调用其他进程中的方法获取数据了:

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
public class MainActivity extends AppCompatActivity {
public static final String TAG = "AIDLDemo";
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IDemoAidlInterface aidl = IDemoAidlInterface.Stub.asInterface(service);
try {
Log.d(TAG, "1 + 2 = " + aidl.add(1, 2));
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, DemoService.class);
bindService(intent, mConnection, BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}

aidl的原理

但是aidl文件又是个什么东西?aidl又到底是怎样工作的呢?

aidl是Android Interface definition language的缩写,实际上它是一中领域特定语言即domain-specific languages,简称DSL,aidl的作用领域是定义安卓接口。感兴趣的同学可以自己去找一下DSL的相关概念,这里就不展开讨论了。

aidl底层是通过binder机制实现的,而且不同需求的binder通信实际上代码是有很大的相似性的。厉害的程序员通常是懒惰的程序员,好的ide通常也会提供各种强大的功能帮助程序员去偷懒。

aidl就是一种帮助我们简化安卓进程间通信代码的工具。android studio会根据aidl定义的接口,帮我们自动生成安卓进程间通信的代码,而我们只需要直接使用它生成的代码就好了,而不用自己去写。

让我们看看aidl究竟帮我们生成了什么样的代码:

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public interface IDemoAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/

public static abstract class Stub extends android.os.Binder implements linjw.demo.aidldemo.IDemoAidlInterface {
private static final java.lang.String DESCRIPTOR = "linjw.demo.aidldemo.IDemoAidlInterface";

/**
* Construct the stub at attach it to the interface.
*/

public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an linjw.demo.aidldemo.IDemoAidlInterface interface,
* generating a proxy if needed.
*/

public static linjw.demo.aidldemo.IDemoAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof linjw.demo.aidldemo.IDemoAidlInterface))) {
return ((linjw.demo.aidldemo.IDemoAidlInterface) iin);
}
return new linjw.demo.aidldemo.IDemoAidlInterface.Stub.Proxy(obj);
}

@Override
public android.os.IBinder asBinder() {
return this;
}

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements linjw.demo.aidldemo.IDemoAidlInterface {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

@Override
public int add(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

public int add(int a, int b) throws android.os.RemoteException;
}

它帮忙我们生成了我们在aidl中定义的IDemoAidlInterface接口,并且生成了一个抽象内部类IDemoAidlInterface.Stub去实现和安卓进程间通信相关的代码。而我们只需要继承IDemoAidlInterface.Stub实现具体的业务代码(add方法)就好:

1
2
3
4
5
6
7
8
public interface IDemoAidlInterface extends android.os.IInterface {
public static abstract class Stub
extends android.os.Binder
implements linjw.demo.aidldemo.IDemoAidlInterface {

...
}
public int add(int a, int b) throws android.os.RemoteException;
}

服务端通信原理

在服务端,我们只需要继承IDemoAidlInterface.Stub并完成add方法的功能代码就可以了。当客户端通过aidl调用服务端代码的时候,服务端的add方法就会被调用:

1
2
3
4
5
6
7
8
public IBinder onBind(Intent intent) {
return new IDemoAidlInterface.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
}

但是add究竟是为什么会被调用的呢?奥秘就在IDemoAidlInterface.Stub.onTransact()方法中。onTransact是android.os.Binder的一个方法。客户端将想要调用的服务端的方法、参数等序列化之后通过系统级别的Binder驱动程序传给服务端,然后服务端在将它们反序列化获取想要调用的方法还有传入的参数。而onTransact就是这个反序列化的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

code就代表了客户端想要执行的操作,当它是TRANSACTION_add的时候就代表客户端想调用服务端的add方法。可以从传过来的Parcel中反序列化出传入的两个相加数,然后调用实际的add方法,即this.add(_arg0, _arg1),最后将计算出来的值写入reply中序列化之后传回给客户端。客户端就可以从这个reply中反序列化中出计算的结果。

TRANSACTION_add是一个int,是IDemoAidlInterface接口定义的第一个方法:

1
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

客户端原理

客户端的代码比服务端会复杂一点点。首先从IDemoAidlInterface.Stub.asInterface方法开始看,我们可以通过它获取到一个IDemoAidlInterface:

1
IDemoAidlInterface aidl = IDemoAidlInterface.Stub.asInterface(service);

它的代码是这样的:

1
2
3
4
5
6
7
8
9
10
public static linjw.demo.aidldemo.IDemoAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof linjw.demo.aidldemo.IDemoAidlInterface))) {
return ((linjw.demo.aidldemo.IDemoAidlInterface) iin);
}
return new linjw.demo.aidldemo.IDemoAidlInterface.Stub.Proxy(obj);
}

它会判断传进来的IBinder是不是IDemoAidlInterface的一个实例,如果是的话就直接将它返回,不是的会就会用它去创建一个代理。

但这个判断有什么用呢?什么时候IBinder它会是一个IDemoAidlInterface的实例什么时候又不是呢?

我们写的service不外乎给其他应用使用和给应用内部使用。给其他应用使用的service因为进程不同不能直接传递对象,所以需要将一个对象先序列化再反序列化去实现进程间的传递。但是有一些服务比如播放器的播放服务,很多时候就只是应用内部在使用而已,是进程内的通信(或者说只是线程间的通信)。其实不涉及跨进程通信,可以直接传递,不用经过序列化和反序列化这样耗时的操作。

如果是进程内的通信,传入的IBinder其实是IDemoAidlInterface的一个实例,所以直接返回将它返回就好。但如果是进程间的通信,就不会是是IDemoAidlInterface的实例了,而是一个用于进程间通信的对象了(具体是什么我们可以不用关心)。这个对象没有实现IDemoAidlInterface.add()方法,所以需要通过一些特殊的手段调用到服务端的add方法:

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
private static class Proxy implements linjw.demo.aidldemo.IDemoAidlInterface {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

@Override
public int add(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

它最重要的代码是IDemoAidlInterface.Stub.Proxy.add()这个方法。它将传入的a、b参数序列化到_data这个Parcel中,然后再通过之前传入的IBinder的transact()将它们传递到service中。注意看,我们还指定了调用Stub.TRANSACTION_add这个方法。上一节服务端获取到的code就是这里指定的。然后服务端将计算到的结果序列化到_reply中,客户的这里再将_reply反序列化得到计算结果返回。

aidl原理图

aidl的原理可以用下面的图来表示:

使用aidl传递复杂数据类型

有时候我需要传递一些复杂的数据类型比如自定义的类,aidl也是支持的。但是因为aidl传递数据都是通过序列化实现的,所以aidl要求传递的类必须实现Parcelable接口。比如我们定义一个Data类:

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
public class Data implements Parcelable {
public String data;

public Data() {
}

public Data(String data) {
this.data = data;
}

protected Data(Parcel in) {
data = in.readString();
}

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

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

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

@Override
public void writeToParcel(Parcel dest, int flags) {
}

public void readFromParcel(Parcel in) {
data = in.readString();
}
}

然后需要新建一个Data.aidl文件声明这个类:

1
2
package linjw.demo.aidldemo;
parcelable Data;

最后在IDemoAidlInterface.aidl中添加接口:

1
2
3
4
5
6
7
8
9
10
package linjw.demo.aidldemo;
import linjw.demo.aidldemo.Data;

interface IDemoAidlInterface {
int add(int a, int b);

void setData(in Data data);

void getData(out Data data);
}

输入参数和输出参数

相信大家都看到了in、out这两个关键字了,他们是用来标识这个参数是输入参数还是输出参数的。我们直接可以看一下生成的代码可以很容易看出他们的作用,先看看服务端的Stub. onTransact()方法:

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
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}

case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_setData: {
data.enforceInterface(DESCRIPTOR);
linjw.demo.aidldemo.Data _arg0;
if ((0 != data.readInt())) {
_arg0 = linjw.demo.aidldemo.Data.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}

this.setData(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getData: {
data.enforceInterface(DESCRIPTOR);
linjw.demo.aidldemo.Data _arg0;
_arg0 = new linjw.demo.aidldemo.Data();
this.getData(_arg0);
reply.writeNoException();
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

setData只是简单的将参数返序列化出来传给功能代码,但是getData除了调用功能代码之外,还会将返回值写入reply中传回给客户端。

同时我们也注意到了服务端所有的调用都是在onTransact中分配的,所以需要一个code去标识客户端到底想要调用的是哪一个方法。

我们再来看看客户端的生成代码,也能看到getData方法有从_reply中反序列化出Data来:

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
private static class Proxy implements linjw.demo.aidldemo.IDemoAidlInterface {

...

@Override
public void setData(linjw.demo.aidldemo.Data data) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {

_data.writeInterfaceToken(DESCRIPTOR);
if ((data != null)) {
_data.writeInt(1);
data.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_setData, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}

@Override
public void getData(linjw.demo.aidldemo.Data data) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {

_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getData, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
data.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
}
}