《Android AIDL入门指南:跨进程通信的钥匙》
Android AIDL入门指南:跨进程通信的钥匙
什么是AIDL?
AIDL(Android Interface Definition Language)是Android系统专门为跨进程通信设计的一种接口定义语言。
为什么需要AIDL?
Android 中每个应用默认运行在独立的进程中(沙箱机制),进程间内存隔离。
当需要在不同进程间传递复杂数据或调用方法时(如:应用与系统服务、不同应用间、同一应用的多进程组件间),必须通过 IPC 机制。
AIDL 是 Android 提供的 IPC 解决方案之一,允许定义跨进程的接口,自动生成序列化/反序列化代码。
核心概念四要素
- AIDL 接口定义文件 (*.aidl)
- 服务端实现 (Service)
- 客户端调用 (Activity/Fragment 等)
AIDL 接口定义文件
作用:定义跨进程通信的“契约”(Contract)。
内容:声明服务端暴露给客户端调用的方法、参数和返回值类型。
规则:
支持基本数据类型(int, long, boolean 等)、String、CharSequence、List、Map。
自定义对象必须实现 Parcelable 接口,并单独定义 .aidl 文件声明。
可以嵌套定义其他 AIDL 接口作为参数(用于回调)。
服务端实现 (Service)
作用:具体实现 AIDL 接口定义的功能。
关键类:
Service:承载 IPC 服务的组件。
Stub 类:AIDL 编译器自动生成的抽象类,继承自 Binder 并实现了 AIDL 接口,服务端需要继承
Stub 并实现方法。
实现要点:
在 Service 的 onBind() 方法中返回 Stub 的实现类实例。
注意 线程安全:AIDL 方法可能被多个客户端同时调用,需考虑同步。
管理客户端注册的回调(监听器)时,使用 RemoteCallbackList 代替普通集合。
客户端调用 (Activity/Fragment 等)
作用:绑定服务并调用远程方法。
关键步骤:
绑定服务:通过 bindService() 连接服务端。
转换接口:将服务端返回的 IBinder 对象转换为 AIDL 接口类型(通过 Stub.asInterface())。
调用方法:像调用本地方法一样使用 AIDL 接口。
处理断开连接:实现 ServiceConnection 回调。
注意:
所有 AIDL 方法调用都是同步的(会阻塞客户端线程),应在子线程中调用或使用 oneway 异步调用。
客户端需要捕获 RemoteException(跨进程调用可能失败)。
第四要素(高级)
在实际开发中,还需考虑 数据序列化(Parcelable)和 回调机制:
自定义 Parcelable 对象:跨进程传递的对象必须实现 Parcelable 接口。
双向通信(回调):服务端主动通知客户端,需定义另一个 AIDL 回调接口,客户端实现后注册到服务端。
手把手实战
手把手实现两个应用之间的跨进程通信,广播相对简单,不做概述,主要来实战aidl
首先我们的愿景是aidl的服务端直接提供一个aar包给客户端调用,服务端只需要关注方法的调用就行,无需关注service的绑定,减低客户端对接的成本,其实无论我们做什么功能,给到别人用的,优先原则就是上层使用越简单越好!
现在直接上干货,如何定义一个aar包
上图在示例demo项目当中新建一个lib库(demoaar)
在demoaar当中新建了一个aidl文件DemoService,里面定义了相关方法给到第三方应用使用
plugins {
id 'com.android.library'
}
android {
namespace 'com.auto.sample'
compileSdk 33
defaultConfig {
minSdk 26
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
android.libraryVariants.all{ variant ->
variant.outputs.all {
def fileName = "car.demo.sdk.v1.0.7.aar"
outputFileName = fileName
}
}
}
dependencies {
}
上面是demoaar的build.gradle文件
其中打包出来的aar的名字就是car.demo.sdk.v1.0.7.aar,这个版本号可以根据自己的情况定,一般初始版本可以定为car.demo.sdk.v1.0.0.aar
这样构建这个lib的时候就会打包出来
在定义aidl的文件有个特别重要的点要注意 那就是包名
这个aidl 的包名是com.auto.sample
项目app模块的包名一定也是com.auto.sample 不然跨进程通信的是调不通的
此外我们还需要app模块下也要建立一个跟aarLib库一样的aidl文件
上面是app模块下的aidl
接下来我们就来看实际案例
假设现在有个需求,我需要拿到服务端app的一条对象数据,这个对象我在服务端定义成DemoData
public class DemoData implements Parcelable {
public String name;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
}
public void readFromParcel(Parcel source) {
this.name = source.readString();
}
public DemoData() {
}
protected DemoData(Parcel in) {
this.name = in.readString();
}
public static final Creator<DemoData> CREATOR = new Creator<DemoData>() {
@Override
public DemoData createFromParcel(Parcel source) {
return new DemoData(source);
}
@Override
public DemoData[] newArray(int size) {
return new DemoData[size];
}
};
}
想要DemoData能够正确下发到客户端,那这个数据一定要是序列化的。
对于这个DemoData 也需要定义好aidl
同样app模块一样需要定义
注意包名 也是需要各个模块一样保持一致
app模块:com.auto.data
demoaar模块:com.auto.data
data模块:com.auto.data
定义好DemoData好后,就需要上面DemoData的bean对象,这个DemoData包名也是com.auto.data
data模块看下面图
做好这些工作之后,在app模块来实现代理service实现aidl的方法
public class DemoService extends Service {
private RemoteCallbackList<DemoServiceCallback> mCallbackList = new RemoteCallbackList<>();
/**
* 同步锁
*/
private final ReentrantLock lock = new ReentrantLock();
private final IBinder mDemoIBinder = new com.auto.sample.DemoService.Stub() {
@Override
public DemoData getDemoData() throws RemoteException {
try {
DemoData demoData = new DemoData();
demoData.name = "这是服务端的数据";
return demoData;
} catch (Exception e) {
Log.e("DemoService", "DemoService:getDemoData error : " + e.getMessage());
return null;
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mDemoIBinder;
}
}
在 AndroidManifest里面进行注册这个service
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.auto.sample">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Common"
tools:targetApi="31">
<service
android:name=".service.DemoService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.autolink.account.service" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
从上面的代理的DemoService的类中看到我们实现了这个getDemoData这个方法
@Override
public DemoData getDemoData() throws RemoteException {
try {
DemoData demoData = new DemoData();
demoData.name = "这是服务端的数据";
return demoData;
} catch (Exception e) {
Log.e("DemoService", "DemoService:getDemoData error : " + e.getMessage());
return null;
}
}
这意味着客户端调用这个方法就会返回这个实例,其中这个name的字段就是这是服务端的数据。
现在还需要在aar当中写个manager类,提供直接调用这个方法能力,这样客户端不再关注服务端绑定的问题。
public class DemoServiceManager {
private static final String TAG = "DemoServiceManager";
private static final String TARGET_PACKAGE = "com.auto.sample"; // 服务端应用的包名
private static final String TARGET_SERVICE_CLASS = "com.auto.sample.service.DemoService";
private static volatile DemoServiceManager instance;
private Context context;
private DemoService demoService;
private boolean isBinding = false;
private ServiceConnection connection;
private OnServiceConnectedListener listener;
public interface OnServiceConnectedListener {
void onConnected();
void onDisconnected();
}
private DemoServiceManager() {
}
public static DemoServiceManager getInstance() {
if (instance == null) {
synchronized (DemoServiceManager.class) {
if (instance == null) {
instance = new DemoServiceManager();
}
}
}
return instance;
}
/**
* 初始化并绑定服务(建议在Application或首个Activity的onCreate调用)
*/
public void init(Context context, OnServiceConnectedListener listener,DemoServiceCallback serviceCallback) {
this.context = context.getApplicationContext();
this.listener = listener;
bindService();
}
private void bindService() {
Intent intent = new Intent();
intent.setClassName(TARGET_PACKAGE, TARGET_SERVICE_CLASS);
// 如果服务端定义了权限,可以在此添加
// intent.setPackage(TARGET_PACKAGE); // 可选,确保只匹配该包
connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
demoService = DemoService.Stub.asInterface(service);
isBinding = false;
Log.d(TAG, "Service connected");
if (listener != null) {
listener.onConnected();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
demoService = null;
isBinding = false;
Log.d(TAG, "Service disconnected");
if (listener != null) {
listener.onDisconnected();
}
}
};
boolean success = context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
if (success) {
isBinding = true;
} else {
Log.e(TAG, "bindService failed, ensure service is registered and exported");
}
}
/**
* 调用isConnect方法
* 注意:确保已连接,否则调用会忽略(可改为抛出异常或等待)
*/
public void sendIsConnect(boolean status) {
if (demoService != null) {
try {
demoService.isConnect(status);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
}
} else {
Log.w(TAG, "Service not connected, call init() first and wait for onConnected");
// 可选:自动重新绑定并缓存调用(较复杂,这里不实现)
}
}
public DemoData getDemoData() {
if (demoService != null) {
try {
return demoService.getDemoData();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
return null;
}
} else {
Log.w(TAG, "Service not connected, call init() first and wait for onConnected");
// 可选:自动重新绑定并缓存调用(较复杂,这里不实现)
return null;
}
}
public void registerCallback(DemoServiceCallback serviceCallback) {
if (demoService != null) {
try {
demoService.registerCallback(serviceCallback);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
}
} else {
Log.w(TAG, "register fail");
// 可选:自动重新绑定并缓存调用(较复杂,这里不实现)
}
}
public void sendData() {
if (demoService != null) {
try {
demoService.sendData();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
}
} else {
Log.w(TAG, "sendData fail");
// 可选:自动重新绑定并缓存调用(较复杂,这里不实现)
}
}
/**
* 释放资源,解绑服务
*/
public void release() {
if (demoService != null && context != null && connection != null) {
context.unbindService(connection);
demoService = null;
isBinding = false;
connection = null;
}
}
}
直接调用客户端集成aar,调用getDemoData这个方法,在客户端就可以拿到服务端的数据
想必看到这个manager提供的方法,包括设置,获取,回调一整套交互方法都在这个manager当中,这里主要挑了个getDemoData为例子,具体回调相关通信方式我会提供相关demo,大家直接把demo跑起来就可以实现通信了。
客户端调用方式:

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DemoServiceManager.getInstance().init(this, new DemoServiceManager.OnServiceConnectedListener() {
@Override
public void onConnected() {
Log.d("dddd", "onConnected");
DemoServiceManager.getInstance().registerCallback(callback);
}
@Override
public void onDisconnected() {
Log.d("dddd", "onDisconnected");
}
});
initListener();
}
private final DemoServiceCallback callback = new DemoServiceCallback.Stub() {
@Override
public void resultCallBack(DemoData demoData) throws RemoteException {
Log.d("dddd", "demoData" + demoData.name);
}
};
private void initListener() {
findViewById(R.id.rootLinear).setOnClickListener(v -> {
DemoData demoData = DemoServiceManager.getInstance().getDemoData();
String name = demoData.name;
Log.d("dddd", name);
});
}
}
关注setOnClickListener点击事件,这样就能获取到服务端的数据了,测试的时候先把service的app运行起来,然后再运行client的app。
同样是跨进程通信,广播和AIDL为什么会选择AIDL通信?
| 广播 (Broadcast) | AIDL |
|---|---|
| 发布-订阅模式(一对多)发布-订阅模式(一对多) | 客户端-服务端模式(一对一或一对多) |
| 单向:发送方 → 接收方 | 双向:客户端 ↔ 服务端 |
| 弱,发送后不关心结果 | 强,可等待服务端响应 |
| 简单数据(Bundle 支持的基本类型和 Parcelable) | 复杂对象、接口回调、自定义类型 |
| Intent 有传输大小限制(约 1MB) | 理论上无严格限制,但大对象会影响性能 |
| 一次性传递,接收方被动接收 | 可持续交互,多次方法调用 |
| 高,涉及系统广播队列、多应用唤醒 | 低,直接 Binder 内核调用 |
| 较慢(毫秒级) | 很快(微秒级) |
| 重,可能唤醒多个应用进程 | 精细的接口级权限控制 |
| 一次性,无法维持连接状态 | 可保持长连接,管理会话状态 |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)