AIDL(Android Interface Definition Language)是 Android 实现 跨进程通信(IPC) 的核心机制,常用于 Service 与 Client 之间的数据交互(如系统服务调用)。以下是详细实现步骤:
在 src/main/aidl/ 目录下创建 .aidl 文件,声明接口方法:
示例:IMyService.aidl
// 包名需与 Java 类一致
package com.example.aidl;
// 定义接口
interface IMyService {
// 基本类型参数(int, String 等可直接使用)
int add(int a, int b);
// 传递自定义对象需实现 Parcelable
void sendUser(in User user);
}in/out/inout:标记参数方向(输入/输出/双向)。- 自定义对象:需实现
Parcelable并单独定义 AIDL(见步骤 2)。
若需传递自定义对象(如 User),需完成以下两步:
User.aidl
package com.example.aidl;
parcelable User; // 声明为 Parcelable 类型User.java
public class User implements Parcelable {
private String name;
private int age;
// 构造方法、getter/setter 省略...
// Parcelable 实现
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
@Override
public int describeContents() {
return 0;
}
}public class MyService extends Service {
private final IMyService.Stub binder = new IMyService.Stub() {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public void sendUser(User user) {
Log.d("Service", "收到用户: " + user.getName());
}
};
@Override
public IBinder onBind(Intent intent) {
return binder; // 返回 Stub 对象
}
}<service
android:name=".MyService"
android:exported="true" <!-- 允许其他进程绑定 -->
android:process=":remote" /> <!-- 指定运行在独立进程 -->private IMyService myService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 将 Binder 对象转换为 AIDL 接口
myService = IMyService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
}
};
// 绑定服务
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);// 调用基本类型方法
int result = myService.add(1, 2);
// 调用自定义对象方法
User user = new User("Alice", 25);
myService.sendUser(user);unbindService(connection);-
线程模型:
- AIDL 方法默认运行在 Binder 线程池(非主线程),需自行处理线程同步。
- 若需在主线程回调,使用
Handler或runOnUiThread。
-
异常处理:
try { myService.add(1, 2); } catch (RemoteException e) { e.printStackTrace(); }
-
权限控制:
- 在
AndroidManifest.xml中声明自定义权限:<permission android:name="com.example.ACCESS_MY_SERVICE" />
- Service 端校验权限:
@Override public IBinder onBind(Intent intent) { if (checkCallingPermission("com.example.ACCESS_MY_SERVICE") != PERMISSION_GRANTED) { return null; // 拒绝绑定 } return binder; }
- 在
-
Android 11+ 的包可见性:
- 若目标 App 为 Android 11+,需在
AndroidManifest.xml中添加<queries>:<queries> <package android:name="com.example.serviceapp" /> </queries>
- 若目标 App 为 Android 11+,需在
+-------------------+ +-------------------+ +-------------------+
| Client App | | Binder | | Service App |
| | | (内核驱动) | | |
| 1. bindService() | ----> | 2. 跨进程绑定 | ----> | 3. onBind() |
| | | | | |
| 4. 调用 AIDL 方法 | ----> | 5. 跨进程调用 | ----> | 6. 执行方法并返回 |
| | | | | |
+-------------------+ +-------------------+ +-------------------+
| 方案 | 适用场景 | 复杂度 | 性能 | 支持数据类型 |
|---|---|---|---|---|
| AIDL | 高频跨进程调用(如系统服务) | 高 | 高 | 基本类型 + Parcelable 对象 |
| Messenger | 低频单向通信 | 低 | 中 | Message 对象(Bundle) |
| ContentProvider | 结构化数据共享 | 中 | 中 | Cursor/文件 |
| Broadcast | 全局事件通知 | 低 | 低 | Intent 数据 |
- 定义 AIDL 接口:声明跨进程方法。
- 实现 Parcelable(可选):支持自定义对象传输。
- Service 端:继承
Service并返回Stub实现。 - Client 端:绑定 Service 并调用 AIDL 方法。
- 注意事项:线程安全、异常处理、权限控制。
AIDL 是 Android 高性能 IPC 的基石,适合复杂跨进程通信场景(如系统服务开发)。
在 AIDL 跨进程通信中,服务端(Server) 和 客户端(Client) 的角色可以类比为 饭店(服务端) 和 顾客(客户端):
| 角色 | 类比 | 在 AIDL 中的表现 |
|---|---|---|
| 服务端 | 饭店(提供服务的) | 1. 定义菜单(AIDL 接口) 2. 实现菜品(Service 中的具体逻辑) 3. 开门营业(注册 Service) |
| 客户端 | 顾客(享受服务的) | 1. 拿到菜单(获取 AIDL 接口) 2. 点菜(调用远程方法) 3. 吃完结账(解绑 Service) |
- 代码位置:通常是一个独立的 App 或模块(如系统服务)。
- 关键步骤:
- 定义菜单:编写
.aidl文件(如IMyService.aidl),声明哪些方法可以被远程调用。 - 做菜:在
Service中实现 AIDL 接口的具体逻辑(如计算、数据库查询)。 - 开门营业:在
AndroidManifest.xml中声明 Service,并设置android:process让它运行在独立进程。
- 定义菜单:编写
// 服务端:MyService.java
public class MyService extends Service {
// 实现 AIDL 接口
private final IMyService.Stub binder = new IMyService.Stub() {
@Override
public int add(int a, int b) {
return a + b; // 实际的服务逻辑
}
};
@Override
public IBinder onBind(Intent intent) {
return binder; // 返回“菜单”
}
}- 代码位置:另一个 App 或同一 App 的不同进程(如主进程调用后台服务)。
- 关键步骤:
- 拿到菜单:通过
bindService()绑定服务端的 Service,获取 AIDL 接口的代理对象。 - 点菜:调用 AIDL 接口的方法(如
add(1, 2)),就像调用本地方法一样。 - 结账:用完服务后解绑,避免资源泄漏。
- 拿到菜单:通过
// 客户端:MainActivity.java
private IMyService myService;
// 绑定服务
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 将 Binder 对象转换为 AIDL 接口(拿到菜单)
myService = IMyService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
}
};
// 点击按钮调用远程方法
button.setOnClickListener(v -> {
try {
int result = myService.add(1, 2); // 点菜:调用服务端方法
Log.d("Client", "结果:" + result);
} catch (RemoteException e) {
e.printStackTrace();
}
});
// 绑定服务(进店点餐)
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
// 记得在 onDestroy() 中解绑!- 服务端:定义功能 + 实现逻辑(像饭店后厨)。
关键文件:.aidl、Service实现类、AndroidManifest.xml声明。 - 客户端:绑定服务 + 调用方法(像顾客点菜)。
关键代码:bindService()、ServiceConnection、AIDL 接口调用。
可以! 比如:
- 主进程(客户端)调用后台进程(服务端)的 Service。
- 通过设置
android:process=":remote"让 Service 运行在独立进程。
- 服务端必须将 .aidl 文件 复制给客户端(保持包名和内容完全一致)。
- 客户端通过
IMyService.Stub.asInterface(binder)获取接口后,就能看到所有定义的方法。
- 客户端会触发
onServiceDisconnected(),此时应重试绑定或提示用户。 - 通过
try-catch捕获RemoteException处理异常。
服务端(饭店) 客户端(顾客)
-------------- --------------
1. 定义菜单(AIDL)
2. 做菜(实现 Service)
3. 开门营业(注册 Service)
4. 进店(bindService)
5. 点菜(调用 add(1,2))
6. 上菜(返回结果)
7. 吃完结账(unbindService)
通过这个类比,你应该能清晰区分 AIDL 中服务端和客户端的角色了!
使用Android接口定义语言(AIDL)进行跨进程通信(IPC),核心是让两个不同进程的应用通过一个预先定义好的接口进行方法调用。整个过程可以分为服务端和客户端两个角色,主要包含以下五个基本步骤:
这是通信的起点,你需要定义一个双方都认可的“通话协议”。
- 创建文件:在Android项目的
src/main/目录下新建一个aidl文件夹,然后在此文件夹中创建以.aidl为扩展名的文件(例如IRemoteService.aidl)。 - 定义接口:在
.aidl文件中,使用类似Java的语法定义可供客户端调用的方法。// IRemoteService.aidl package com.example.server; interface IRemoteService { int getPid(); void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); }
- 编译生成Java文件:在你构建项目(Make Project或Rebuild)后,Android Gradle插件会自动根据你的
.aidl文件,在build/generated/目录下生成一个同名的Java接口文件(如IRemoteService.java)。这个文件包含了实现IPC通信所需的Stub类、Proxy代理类等核心代码。
注意:
.aidl文件支持的数据类型包括Java基本类型、String、CharSequence、List、Map以及其他AIDL接口或你自定义且实现了Parcelable接口的对象。
服务端需要“接听电话”,并真正执行那些被定义好的方法。
- 创建Service:创建一个继承自
Service的类。 - 实现接口方法:在Service内部,创建一个Binder对象。这个Binder对象需要继承上一步编译生成的
IRemoteService.Stub抽象类,并实现你在.aidl中定义的所有方法(如getPid()和basicTypes())。这些方法里写的才是真正的业务逻辑。// RemoteService.java public class RemoteService extends Service { @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { // 返回实现了AIDL接口的Binder对象 return binder; } private final IRemoteService.Stub binder = new IRemoteService.Stub() { @Override public int getPid() throws RemoteException { return android.os.Process.myPid(); } @Override public void basicTypes(...) throws RemoteException { // 实现具体逻辑 } }; }
- 在
onBind()中返回:在Service的onBind()生命周期方法中,返回上面创建的Binder实例。当客户端绑定服务时,这个Binder对象会被传递给客户端。
客户端需要“拨号”并建立连接,才能使用服务端的功能。
- 复制AIDL文件:为了让客户端也能“认识”这个接口,你需要将服务端定义的
.aidl文件(注意是整个文件结构,包括包名路径)完整地复制到客户端的src/main/aidl目录下。 - 创建ServiceConnection:创建一个
ServiceConnection对象,用于监听与远程服务连接的状态变化。 - 获取接口对象:在
onServiceConnected()回调方法中,你会收到一个IBinder对象。这是系统传递过来的、代表远程服务的“电话线”。你需要通过IRemoteService.Stub.asInterface(service)将这个IBinder对象转换成你可以直接调用的AIDL接口对象。// 在Activity或Fragment中 private IRemoteService remoteService; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 将IBinder转换为AIDL接口 remoteService = IRemoteService.Stub.asInterface(service); Log.d("Client", "服务连接成功"); } @Override public void onServiceDisconnected(ComponentName name) { remoteService = null; Log.d("Client", "服务断开连接"); } };
现在“电话”已经接通,可以开始通话了。
- 调用bindService():创建显式Intent(指定服务端应用的包名和Service类的完整路径),然后调用
bindService()方法启动并绑定远程服务。Intent intent = new Intent(); // 对于Android 5.0及以上版本,必须使用显式Intent intent.setComponent(new ComponentName("com.example.server", "com.example.server.RemoteService")); bindService(intent, connection, Context.BIND_AUTO_CREATE);
- 调用远程方法:一旦
onServiceConnected被触发,你得到了remoteService对象,就可以像调用本地方法一样调用远程服务中定义的方法了。需要注意的是,这些方法是在服务端的Binder线程池中执行的,可能耗时,所以需要注意线程管理,避免在UI线程直接调用可能耗时的操作。// 当按钮被点击时 public void onGetPidClick(View view) { if (remoteService != null) { try { int remotePid = remoteService.getPid(); Toast.makeText(this, "远程服务PID: " + remotePid, Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } } }
这是让服务能被外部发现的最后一步。
- 声明Service:在服务端应用的
AndroidManifest.xml文件中,声明你的RemoteService。 - 配置Intent过滤器 (可选但推荐):为了让客户端更容易地找到服务,可以给Service配置一个
<intent-filter>,并指定一个唯一的action名称。同时,如果希望其他应用也能访问,需要将android:exported属性设置为true。<service android:name=".RemoteService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.example.server.REMOTE_SERVICE" /> </intent-filter> </service>
使用AIDL进行跨进程通信,就像两个人用对讲机通话:
- 制定通话规则(定义
.aidl接口)。 - 一方准备好接收信息并执行动作(服务端实现
Stub类)。 - 另一方获取对方的通话频道(客户端复制AIDL,绑定
Service,通过Stub.asInterface获取代理对象)。 - 开始通话(通过代理对象调用方法)。