无处不在的 "AIDL"

一、开篇

我们都知道,Android 系统中的进程之间是不能共享内存的,那么两个不同的应用程序之间如何通讯?这就涉及到跨进程进程通讯的方式之一:安卓接口定义语言:AIDL( Android Interface Definition Language)。

二、AIDL 使用

既然是跨进程通信,那么就需要两个进程,接下来我们分别整出一个客户端 client,一个服务端 server,然后实现客户端从服务端获取数据的功能,从中了解 AIDL 如何使用!

2.1 AIDL 服务端

2.1.1 创建服务端

mFHOaT.png

2.1.2 创建 AIDL 文件

mFbtzj.png

IMyAidlInterface.aidl

1
2
3
4
5
6
7
8
9
10
11
// IMyAidlInterface.aidl
package com.example.service;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

// 添加一个方法
String getString();

}

2.1.2 生成 JAVA 文件

执行 AS –> Build –> Make Module 'app' 会在 server\build\generated\aidl_source_output_dir 路径下生成 IMyAidlInterface.java 文件。

mFqEmq.png

2.1.3 创建 MyService

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
package com.example.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class MyService extends Service {

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();
}

@Override
public IBinder onBind(Intent intent) {
// 返回 MyBind 实例
return new MyBind();
}

// MyBind 继承自 IMyAidlInterface.Stub
class MyBind extends IMyAidlInterface.Stub {
@Override
public String getString() throws RemoteException {
String string = "这是一条从服务端返回的字串";
return string;
}
}
}

2.2 AIDL 客户端

2.2.1 复制 AIDL 文件

Server 端的 AIDL 文件复制到 Client 端,请注意:保证路径一摸一样,并且包名需要一致

mFjGxe.png

2.2.2 修改 activity_main.xml

添加两个按钮:BIND SERVICEUNBIND SERVICE,一个 TextView

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/bind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="BIND SERVICE" />

<Button
android:id="@+id/unbind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="UNBIND SERVICE" />

<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="等待服务端返回数据..."/>

</LinearLayout>

2.2.3 修改 MainActivity.java

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
package com.example.aidl;

import androidx.appcompat.app.AppCompatActivity;
... ...

import com.example.service.IMyAidlInterface;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private Button bindService, unbindService;
private TextView textView;
private IMyAidlInterface myAidlInterface;

private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
myAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
try {
String string = myAidlInterface.getString();
textView.setText(string);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
myAidlInterface = null;
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = (TextView) findViewById(R.id.text_view);
bindService = (Button) findViewById(R.id.bind_service);
unbindService = (Button) findViewById(R.id.unbind_service);

bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}

@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bind_service:
Intent intent = new Intent();
Log.d("marco", "bind_service setAction()");
intent.setComponent(new ComponentName("com.example.service",
"com.example.service.MyService"));
bindService(intent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
unbindService(connection);
break;
}
}
}

2.3 数据交互

通过上面的代码,服务端和客户端就建立了连接,我们运行下项目,看下实际结果:

mk9OdH.png

执行 BIND SERVICE 后:

mkCDTH.png

可以发现,客户端从服务端获取到了数据,这也证明通过 AIDL 机制,两个应用(进程)之间可以进行通信。


三、AIDL 原理

当我们创建了这个接口后,系统会自动生成其对应的 Binder类,它继承了 IInterface, 内部有一个静态抽象类 Stub 和 Stub 内部的 Proxy类。其中 Stub 继承了 Binder 类,所以 AIDL 中的 Stub 即为一个 Binder 对象,在服务端实现该接口后,支持在客户端远程调用。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// IMyAidlInterface.java,对于这个文件的深入分析我们后面会详细讲解,这边只需要大概看下

package com.example.service;
// Declare any non-default types here with import statements

// IMyAidlInterface 继承了 IInterface 接口
public interface IMyAidlInterface extends android.os.IInterface
{
/** Default implementation for IMyAidlInterface. */
public static class Default implements com.example.service.IMyAidlInterface
{
@Override public java.lang.String getString() throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
// 静态抽象类 Stub,继承了 Binder
public static abstract class Stub extends android.os.Binder
implements com.example.service.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.service.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
// 静态 asInterface 方法
public static com.example.service.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
// queryLocalInterface() 这个方法判断是否同一个进程
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
// 同进程,直接返回继承该 Stub 的 Binder
if (((iin!=null)&&(iin instanceof com.example.service.IMyAidlInterface))) {
return ((com.example.service.IMyAidlInterface)iin);
}
// 不同进程,返回静态代理类
return new com.example.service.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
// 重写 onTransact 方法
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getString:
{
data.enforceInterface(descriptor);
java.lang.String _result = this.getString();
reply.writeNoException();
reply.writeString(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
// 静态内部类 Proxy(代理类)
private static class Proxy implements com.example.service.IMyAidlInterface
{
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 java.lang.String getString() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getString();
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.service.IMyAidlInterface sDefaultImpl;
}
static final int TRANSACTION_getString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.example.service.IMyAidlInterface impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.service.IMyAidlInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public java.lang.String getString() throws android.os.RemoteException;
}

所以,AIDL 定义的接口,除了是一个 接口 以外,它还是一个 Binder 对象,支持在 接口Binder 之间相互转换。

接下来,我们结合上面的 Service 案例,然后剖解 IMyAidlInterface.java 文件,深入探讨 AIDL 的实现原理。

3.1 IMyAidlInterface

我们在 Server 端定义了一个 AIDL 文件:IMyAidlInterface.aidl,并且在其中定义了一个接口:String getString();

然后我们编译后,系统会帮我们自动生成一个 IMyAidlInterface.java 文件,简化如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// IMyAidlInterface.java

package com.example.service;
// Declare any non-default types here with import statements

// IMyAidlInterface 继承了 IInterface 接口
public interface IMyAidlInterface extends android.os.IInterface
{
... ...
// 静态抽象类 Stub,继承了 Binder
public static abstract class Stub extends android.os.Binder
implements com.example.service.IMyAidlInterface
{... ...}
public java.lang.String getString() throws android.os.RemoteException;
}

1、该文件是一个接口文件,继承了 android.os.IInterface 接口;
2、在该接口文件中,定义了在 AIDL 中申明接口的 JAVA 形式:java.lang.String;
3、在该接口文件中,定义了一个静态的抽象类 Stub,该类继承自 Binder;

3.2 IInterface

IInterface 接口是什么,我们也分析一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package android.os;

/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface {
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
IBinder asBinder();
}

1、IInterface 是 Binder 接口的基类。当我们定义一个新的接口,则必须从 IInterface 中继承;
2、asBinder() 用于取得与这个接口有联系的 Binder 对象;

3.3 Stub

静态抽象子类 Stub 是个重头戏,我们需要好好研究一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.service.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.service.IMyAidlInterface";

public Stub() {... ...}

public static com.example.service.IMyAidlInterface asInterface(android.os.IBinder obj) {... ...}

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

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
int flags) throws android.os.RemoteException {... ...}

private static class Proxy implements com.example.service.IMyAidlInterface {... ...}

}

1、该类实现了 asBinder 方法,此方法可将接口转化为 Binder;
2、该类提供了 asInterface 方法,此方法可以将 Binder 转化为 IMyService 实现类;
3、该类继承了 android.os.Binder 类,重写了 Binder 类的 onTransact 方法。该方法的作用是,接收客户端发来的请求,并将请求分发至各个接口。
4、该类中自定义了一个静态类 Proxy,实现了 IMyAidlInterface;

3.4 Proxy

我们看下 Proxy 类:它作为一个代理,将接收到的请求,转发给客户端。

1
2
3
4
5
6
7
8
9
10
private static class Proxy implements com.example.service.IMyAidlInterface
{
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 java.lang.String getString() throws android.os.RemoteException
{... ...}
public static com.example.service.IMyAidlInterface sDefaultImpl;
}

1、该类实现了 IMyAidlInterface 继承自 IInterface 的方法 asBinder;
2、该类的构造函数,以 Binder 作为传参,保存为成员变量;
3、该类实现了 IMyAidlInterface 中定义的 getString() 方法;

3.5 getString()

我们看下 getString 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override public java.lang.String getString() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 通过 transact 方法将信息传送到服务端
boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getString();
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

3.6 流程小结

结合 Service 案例,我们看看 客户端服务端 之间是如何通信的:

    ✎ (1)客户端调用 bindService 绑定服务后,将触发 Service 的 onBind 监听方法。该方法将调用 asBinder 方法,返回一个 Binder 对象。
    ✎ (2)客户端将通过 onServiceConnected 回调函数,获取到该 Binder 对象(以传参的形式传入)。
    ✎ (3)客户端获取到 Binder 对象后,调用 stub.asInterface 方法,将其转换为 service 实现类的对象
    ✎ (4)在 asInterface 方法中,将判断 service 与当前进程,是否在同一进程中。若是,则返回 stub 本身,否则返回 stub.proxy,返回结果将作为 Service 实现类的实例。
    ✎ (5)在通过 Service 实现类的实例调用接口方法时,若为同一进程,则直接调用方法本身。若为跨进程,则调用 stub.proxy 的对应接口方法,通过 Transact 方法将信息传送到服务端。此时,客户端将挂起,等待结果返回。
    ✎ (6)服务端接收到信息,通过 onTransact() 方法,根据方法的唯一标识,将信息转发至各对应方法。
    ✎ (7)信息处理完成后,再由服务端 onTransact 返回结果。