Android AIDL 了解与使用

AIDL简介

AIDL(Android Interface Definition Language, Android 接口定义语言)
用于定义C/S体系结构中Server端可以提供的服务调用接口,框架层提供的Java系统服务接口大多由AIDL语言定义。
Android提供了AIDL工具,可将AIDL文件编译成Java文件。提高服务开发的效率

程序员可以利用AIDL自定义编程接口,在客户端和服务端之间实现进程间通信(IPC)。
在Android平台上,一个进程通常不能访问另外一个进程的内存空间,因此,Android平台将这些跨进程访问的对象分解成操作系统能够识别的简单对象。
并为跨应用访问而特殊编排和整理这些对象。用于编排和整理这些对象的代码编写起来十分冗长,所以Android的AIDL提供了相关工具来自动生成这些代码。

开发人员只需要在AIDL文件中定义Server端可以提供的服务方法,AIDL工具便可将其转化为Java文件。转化后的Java文件包含C/S体系结构的以下内容:

  • 服务接口 (IPowerManager)
  • 服务在Client端的代理(Proxy)
  • 服务存根(Stub)
  • Binder类型与IIterface类型的转换接口(asInterface 和 asBinder 方法)
  • 服务方法请求码

AIDL意义

AIDL工具建立了基于Binder的C/S体系结构的通用组件;开发者可以专注于开发服务的功能,而不需理会具体的通信结构,提高效率。

应用示例

根据上文我们可以知道,我们创建两个apk,一个作为服务提供方,一个作为AIDL服务调用方。

AIDL服务提供方代码

首先是AIDL服务提供方主要文件目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
main/aidl/
`-- com
`-- rustfisher
`-- ndkproj
`-- ITomInterface.aidl // AIDL代码

main/java
`-- com
`-- rustfisher
|-- tom
| `-- TomService.java // 对应的Service

build/generated/source/aidl/
`-- debug
`-- com
`-- rustfisher
`-- ndkproj
`-- ITomInterface.java // 工程编译后AIDL生成的Java文件 提供给调用方

新建AIDL文件并写好接口

进入服务方的工程,右键新建AIDL文件ITomInterface.aidl
文件会默认生成在main/aidl/com/rustfisher/ndkproj

1
2
3
4
5
6
7
8
9
10
// ITomInterface.aidl
package com.rustfisher.ndkproj;

// 文件名应该和接口名相同
// 编写好AIDL文件后可以先编译一次
interface ITomInterface {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String helloAIDL(String name); // 此次使用的方法
}

编写服务方的接口实现代码

com.rustfisher.tom包内创建TomService.java文件;建立内部类TomServiceImpl实现接口的功能

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
import com.rustfisher.ndkproj.ITomInterface;

public class TomService extends Service {
private static final String TAG = "rustApp";

public class TomServiceImpl extends ITomInterface.Stub {

@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

}

@Override
public String helloAIDL(String name) throws RemoteException {
Log.d(TAG, name + " requires helloAIDL()");
return "Hello " + name + ", nice to meet you!";
}
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
return new TomServiceImpl(); // 绑定服务则返回 TomServiceImpl 实例
}
}

服务方在AndroidManifest.xml文件中配置

实现了TomService类后,对此AIDL服务进行配置;在AndroidManifest.xml文件中配置

1
2
3
4
5
<service android:name="com.rustfisher.tom.TomService">
<intent-filter>
<action android:name="com.rustfisher.ndkproj.ITomInterface" />
</intent-filter>
</service>

action里面写上AIDL文件

安装运行此apk到手机上

让服务方运行起来

AIDL调用方代码(客户端)

建立(或进入)AIDL调用方的工程,这里是aidlcaller工程。

主要文件目录

1
2
3
4
5
6
7
8
java/
`-- com
|-- rust
| `-- aidlcaller
| `-- MainActivity.java // 演示用的
`-- rustfisher
`-- ndkproj // 这个路径尽量保持与服务提供方那里的一致
`-- ITomInterface.java // 从服务方那里copy来的

有如下3个步骤:

  • 1.将AIDL服务端生成的Java文件复制到调用方工程里,尽量保持这个Java文件的路径与服务端的一致,便于识别
  • 2.写代码绑定服务,获取AIDL服务对象
  • 3.通过AIDL服务对象完成AIDL接口调用

编写调用方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
import com.rustfisher.ndkproj.ITomInterface;

public class MainActivity extends AppCompatActivity {

private static final String TAG = "rustApp";
ITomInterface mTomService; // AIDL 服务
TextView mTv1;

private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mTomService = ITomInterface.Stub.asInterface(service);// 获取服务对象
mTv1.setClickable(true); // 需要等服务绑定好 再允许点击
Log.d(TAG, "[aidlcaller] onServiceConnected");
}

@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected " + name);
}
};

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

private void initUI() {
mTv1 = (TextView) findViewById(R.id.tv1);
mTv1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
String hello = mTomService.helloAIDL("Jerry");
Log.d(TAG, hello);
} catch (Exception e) {
Log.e(TAG, "mTomService initAIDLService: Fail ", e);
e.printStackTrace();
}
}
});
}

private void initAIDLService() {
// 这个是服务提供方的AndroidManifest action
Intent intent = new Intent("com.rustfisher.ndkproj.ITomInterface");
intent.setPackage("com.rustfisher.ndkproj"); // 服务提供者的包名
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}

测试和效果

点击调用端的View,打出log Hello Jerry, nice to meet you!
服务端apk打印log:Jerry requires helloAIDL()

如果调用失败则抛出 android.os.DeadObjectException

当服务提供方App没有在运行时,调用方去请求服务会失败。

服务端更新后,如果aidl文件没改动,不需要更新生成的Java文件
如果服务端apk被卸载,调用端使用此服务时会出错

参考资料

Android Binder 机制介绍