一、什么是 AIDL?为什么需要它?
1.1 概念解析
AIDL(Android Interface Definition Language,Android 接口定义语言)是 Android 系统中专门用于实现跨进程通信(IPC) 的机制。简单来说,它就像两个不同应用(或进程)之间的 "通信协议",定义了双方能理解的 "对话规则"------ 哪些方法可以调用、参数格式是什么、返回值类型如何。
在 Android 中,每个应用默认运行在独立的进程中,进程间的内存是相互隔离的(即 "沙箱机制")。这意味着一个应用无法直接访问另一个应用的内存数据,而 AIDL 的作用就是打破这种隔离,让不同进程能安全、高效地交换数据和调用方法。
1.2 适用场景
当你遇到以下需求时,就需要用到 AIDL:
- 应用 A 需要调用应用 B 提供的功能(如支付、地图定位等)
- 单个应用需要拆分多个进程(如后台服务单独运行,避免主进程内存溢出)
- 多应用协作完成某个复杂功能(如多个应用共享同一套数据处理逻辑)
二、案例整体架构
我们将创建一个包含 "服务端" 和 "客户端" 的计算器应用:
服务端 :提供add(加法)和subtract(减法)的计算逻辑,通过 Service 暴露给外部调用
客户端:提供用户界面(输入数字、点击按钮),通过 AIDL 绑定服务端,调用远程计算方法并展示结果
三、 step 1:创建 AIDL 接口文件
AIDL 文件是跨进程通信的 "核心协议",必须在服务端和客户端保持完全一致(包名、文件名、方法定义),否则会通信失败。
3.1 新建 AIDL 文件
在build.gradle.kts
文件中添加AIDL支持配置:
kotlin
buildFeatures {
aidl = true
}
在 Android Studio 中操作步骤:右键main→ New → AIDL → AIDL File 文件名输入ICalculator(注意首字母大写,符合接口命名规范),点击Finish
此时会生成一个默认的 AIDL 文件,我们需要修改其内容,定义加法和减法方法:
cpp
// ICalculator.aidl
package com.example.aidldemo; // 包名必须与项目包名一致
// 声明接口,定义跨进程可调用的方法
interface ICalculator {
// 加法方法:接收两个int参数,返回计算结果
int add(int num1, int num2);
// 减法方法:接收两个int参数,返回计算结果
int subtract(int num1, int num2);
}
3.2 编译生成 Java 文件
AIDL 文件本身不能直接使用,需要让 Android Studio 自动生成对应的 Java 代码(包含 Binder 通信逻辑)。
操作步骤:点击顶部菜单栏的 Build编译
编译完成后,会在app/build/generated/aidl_source_output_dir/
目录下生成ICalculator.java文件(无需手动修改)
四、 step 2:实现服务端 Service
服务端的核心是创建一个Service,并在其中实现 AIDL 接口定义的方法,通过Binder对象将服务暴露给客户端。
4.1 创建 CalculatorService 类
创建一个服务类
修改代码如下:
cpp
package com.example.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class CalculatorService extends Service {
private static final String TAG = "CalculatorService";
// 1. 创建AIDL接口的实现类(Stub是ICalculator.java中自动生成的内部类)
private final ICalculator.Stub mBinder = new ICalculator.Stub() {
// 实现加法方法
@Override
public int add(int num1, int num2) throws RemoteException {
Log.d(TAG, "收到加法请求:" + num1 + " + " + num2);
return num1 + num2; // 实际计算逻辑
}
// 实现减法方法
@Override
public int subtract(int num1, int num2) throws RemoteException {
Log.d(TAG, "收到减法请求:" + num1 + " - " + num2);
return num1 - num2; // 实际计算逻辑
}
};
// 2. 当客户端绑定服务时,返回Binder对象(核心:让客户端获取通信入口)
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "客户端绑定服务");
return mBinder; // 返回实现好的Binder对象
}
}
4.2 在 Manifest 中注册 Service
Service 必须在AndroidManifest.xml中注册,否则客户端无法找到服务。添加以下代码到标签内:
cpp
<service
android:name=".CalculatorService"
android:exported="true" <!-- 允许外部应用访问该服务 -->
android:process=":remote"> <!-- 可选:让服务运行在独立进程(跨进程的关键) -->
<!-- 配置Intent过滤器,方便客户端通过Action绑定 -->
<intent-filter>
<action android:name="com.example.aidldemo.CALCULATOR_SERVICE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
android:exported="true":必须设置为true,否则外部应用无法绑定该服务
android:process=":remote":可选配置,让服务运行在名为 "remote" 的独立进程(即使是同一应用,也能模拟跨进程场景)
五、 step 3:实现客户端界面与逻辑
客户端需要完成三个核心操作:绑定服务端 Service → 调用 AIDL 方法 → 展示计算结果。
5.1 创建布局文件(activity_main.xml)
客户端界面需要两个输入框(输入数字)、两个功能按钮(加法 / 减法)、两个服务控制按钮(绑定 / 解绑)和一个结果显示框。布局代码如下:
cpp
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<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.AIDLDemo">
<service
android:name=".CalculatorService"
android:process=":remote"
android:enabled="true"
android:exported="true"></service>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
5.2 实现 MainActivity 逻辑
MainActivity 是客户端的核心,需要处理服务绑定、AIDL 方法调用、用户交互等逻辑。代码如下:
cpp
package com.example.aidldemo;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
// 1. 声明核心变量
private ICalculator mCalculator; // AIDL接口实例(用于调用远程方法)
private EditText etNum1, etNum2; // 数字输入框
private TextView tvResult; // 结果显示框
// 2. 服务连接对象(监听服务绑定/断开状态)
private ServiceConnection mConnection = new ServiceConnection() {
// 服务绑定成功时调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 将IBinder对象转换为AIDL接口实例(关键步骤)
mCalculator = ICalculator.Stub.asInterface(service);
Toast.makeText(MainActivity.this, "服务绑定成功!", Toast.LENGTH_SHORT).show();
}
// 服务意外断开时调用(正常解绑不会触发)
@Override
public void onServiceDisconnected(ComponentName name) {
mCalculator = null; // 清空接口实例,避免空指针
Toast.makeText(MainActivity.this, "服务意外断开!", Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 3. 初始化控件
initViews();
// 4. 绑定按钮点击事件
initClickEvents();
}
// 初始化控件
private void initViews() {
etNum1 = findViewById(R.id.et_num1);
etNum2 = findViewById(R.id.et_num2);
tvResult = findViewById(R.id.tv_result);
}
// 初始化按钮点击事件
private void initClickEvents() {
// 绑定服务按钮
findViewById(R.id.btn_bind).setOnClickListener(v -> bindRemoteService());
// 解绑服务按钮
findViewById(R.id.btn_unbind).setOnClickListener(v -> unbindRemoteService());
// 加法计算按钮
findViewById(R.id.btn_add).setOnClickListener(v -> calculateAdd());
// 减法计算按钮
findViewById(R.id.btn_subtract).setOnClickListener(v -> calculateSubtract());
}
// 5. 绑定远程服务
private void bindRemoteService() {
// 方式1:通过Action绑定(需在Service的Manifest中配置intent-filter)
Intent intent = new Intent();
intent.setAction("com.example.aidldemo.CALCULATOR_SERVICE");
intent.setPackage("com.example.aidldemo"); // 必须指定服务端包名(Android 11+强制要求)
// 方式2:通过组件名绑定(需知道服务端的包名和Service类名)
// Intent intent = new Intent();
// intent.setComponent(new ComponentName(
// "com.example.aidldemo", // 服务端包名
// "com.example.aidldemo.CalculatorService" // 服务端Service类名
// ));
// 绑定服务:BIND_AUTO_CREATE表示服务未启动时自动创建
bindService(intent, mConnection, BIND_AUTO_CREATE);
}
// 6. 解绑远程服务
private void unbindRemoteService() {
if (mCalculator != null) {
unbindService(mConnection); // 解绑服务
mCalculator = null; // 清空接口实例
Toast.makeText(this, "服务已解绑!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "未绑定服务,无需解绑!", Toast.LENGTH_SHORT).show();
}
}
// 7. 调用加法方法
private void calculateAdd() {
// 先判断服务是否已绑定
if (mCalculator == null) {
Toast.makeText(this, "请先绑定服务!", Toast.LENGTH_SHORT).show();
return;
}
try {
// 获取输入框的数字
int num1 = Integer.parseInt(etNum1.getText().toString().trim());
int num2 = Integer.parseInt(etNum2.getText().toString().trim());
// 调用远程服务的add方法(跨进程调用)
int result = mCalculator.add(num1, num2);
// 显示结果
tvResult.setText("计算结果:" + num1 + " + " + num2 + " = " + result);
} catch (NumberFormatException e) {
// 处理输入非数字的异常
Toast.makeText(this, "请输入有效的数字!", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
// 处理跨进程调用失败的异常(如服务崩溃、网络断开等)
e.printStackTrace();
Toast.makeText(this, "调用加法服务失败!", Toast.LENGTH_SHORT).show();
}
}
// 8. 调用减法方法
private void calculateSubtract() {
if (mCalculator == null) {
Toast.makeText(this, "请先绑定服务!", Toast.LENGTH_SHORT).show();
return;
}
try {
int num1 = Integer.parseInt(etNum1.getText().toString().trim());
int num2 = Integer.parseInt(etNum2.getText().toString().trim());
// 调用远程服务的subtract方法
int result = mCalculator.subtract(num1, num2);
// 显示结果
tvResult.setText("计算结果:" + num1 + " - " + num2 + " = " + result);
} catch (NumberFormatException e) {
Toast.makeText(this, "请输入有效的数字!", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
Toast.makeText(this, "调用减法服务失败!", Toast.LENGTH_SHORT).show();
}
}
// 9. Activity销毁时解绑服务(避免内存泄漏)
@Override
protected void onDestroy() {
super.onDestroy();
unbindRemoteService();
}
}
六、 step 4:测试跨进程通信效果

七、 不同应用间的客户端服务器通信
服务器与客户端必须使用完全一致的 AIDL 文件,这是确保二者能正确解析通信数据、实现功能交互的核心前提。
基于上述案例简单修改,将服务端放在otherapp上,客户端在app上,使用相同的AIDL文件,实现效果和上述案例一致
注意:AIDL文件包名和接口名称必须一致