1. AIDL 是什么?
AIDL (Android 接口定义语言) 为开发者提供了一种便捷的方式来利用 Binder 进行进程间通信,而无需深入了解 Binder 底层的复杂实现。
AIDL 提供了一种高层次的、易于使用的接口来定义跨进程通信的规则。开发者通过 AIDL 文件描述需要跨进程访问的接口和方法,Android SDK 工具会在构建应用时自动生成对应的 Java 接口文件。
在需要不同应用的客户端通过 IPC 方式访问服务,并且希望在服务中进行多线程处理时,可以使用 AIDL。
如果无需跨不同应用执行并发 IPC,可以通过实现 Binder 来创建接口。
如果您想执行 IPC,但不需要处理多线程处理,可以使用 Messenger 实现接口。
使用 AIDL 之前,需要了解如何通过 bindService 开启服务,可参考:Android Service 使用详解
接下来通过以下三个方面介绍如何通过 AIDL 进行不同应用间进程间通信:
- 创建 AIDL 文件
- 创建服务端
- 创建客户端并调用
2. 创建 AIDL 文件
创建服务端应用,其名称为 ServerApp
,包名为 com.example.serverapp
。新建 Module(Android Library):
并在其 build.gradle 文件中添加以下配置:
groovy代码解读复制代码android { ... buildFeatures { aidl true } }
在 Android 模式下创建 aidl 文件夹。新建 IUserManager.aidl 文件:
修改 IUserManager.aidl 文件,服务端提供了两个功能,客户端可以通过这两个方法获取用户列表或者添加用户:
java 代码解读复制代码// IUserManager.aidl
package com.example.serverlib;
import com.example.serverlib.User;
// Declare any non-default types here with import statements
interface IUserManager {
List getUser();
void addUser(in User user);
}
User 类创建在 com/example/serverlib/User.java
,需实现 Parcelable 接口。
继续创建 User.aidl 文件声明 User 类实现了 Parcelable 接口:
java 代码解读复制代码package com.example.serverlib;
parcelable User;
重新编译程序,在 build/generated
目录下可以找到 IUserManager 接口文件:
系统生成的 IUserManager:
该生成类的详细逻辑不在本文进行介绍。
至此,aidl 文件创建完成。
总结一下,定义 aidl 文件有以下几点需要注意:
- 必须手动添加
import com.example.serverlib.User;
。 - 需要创建 User.aidl 文件声明 User 类实现了 Parcelable 接口
- 方法可带零个或多个参数,返回值或空值。
- 所有非原语参数都需要指示数据走向的方向标记:in、out 或 inout。原语、String、IBinder 和 AIDL 生成的接口默认为 in,不能是其他方向。
- aidl 文件中包括的所有代码注释都包含在生成的 IBinder 接口中,import 和 package 语句之前的注释除外。
- 可以在 AIDL 接口中定义 String 常量和 int 字符串常量,例如
const int VERSION = 1;
。 - 方法调用由
transact()
代码分派,该代码通常基于接口中的方法索引。由于这会增加版本控制的难度,因此您可以向方法手动配置事务代码:void addUser(in User user) = 1;
。 - 必须使用
@nullable
为可空参数和返回类型添加注解。
3. 创建服务端
在服务端创建两个用户信息,并创建 Binder 对象实现 AIDL 接口文件中的方法,在 onBind 方法中将 Binder 对象返回:
java 代码解读复制代码// com.example.serverapp.UserService
public class UserService extends Service {
private ArrayList mUserList = new ArrayList<>();
@Override
public void onCreate() {
super.onCreate();
mUserList.add(new User("ZhangSan", 18));
mUserList.add(new User("LiSi", 20));
}
public UserService() {
}
private Binder mBinder = new IUserManager.Stub() {
@Override
public List getUserList() throws RemoteException {
return mUserList;
}
@Override
public void addUser(User user) throws RemoteException {
mUserList.add(user);
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
若实现 .aidl
生成的接口,需扩展生成的 Binder 接口(例如 IUserManager.Stub
),并实现从 .aidl
文件继承的方法。
mBinder
是 Stub
类 (Binder) 的一个实例,用于定义服务的 IPC 接口。
注:由于无法保证在主线程上执行传入调用,所以需做好多线程处理。
通过 Service 的 onBind 方法可以将创建的 mBinder 对象公开给客户端,当客户端调用 bindService()
以连接此服务时,客户端的 onServiceConnected()
回调会接收服务的 onBind()
方法返回的 binder
实例。
4. 客户端调用
新建客户端应用 Client,将服务端的 serverlib 包编译生成 arr 文件,将 aar 文件继承到客户端中。
开启服务,添加一个用户,并获取用户列表:
java 代码解读复制代码private void bindService() {
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.example.serverapp", "com.example.serverapp.UserService");
intent.setComponent(componentName);
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IUserManager iUserManager = IUserManager.Stub.asInterface(service);
User user = new User("wang", 20);
try {
iUserManager.addUser(user);
List userList = iUserManager.getUserList();
for (int i = 0; i < userList.size(); i++) {
User user1 = userList.get(i);
Log.d("AIDLActivity", "user: " + user1.getName()+" "+ user1.getAge());
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
boolean result = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Log.i("TAG", "bindService result is " + result);
}
当客户端在 onServiceConnected()
回调中收到 IBinder
时,必须调用 IUserManager.Stub.asInterface(service)
以将返回的参数转换为 IUserManager
类型
执行结果,打印出了远程服务端的所有用户:
评论记录:
回复评论: