AIDL Demo
AIDL文件
// 定义一个 IAdd.aidl 文件,声明一个接口
package com.example.aidldemo;
interface IAdd {
// 定义一个 add 方法,接收两个 int 类型的参数,返回一个 int 类型的结果
int add(int x, int y);
}
服务器端文件
AddService.java
// 在服务端的 AddService.java 中,重写 onBind 方法,返回从 Intent 中获取的 Binder 对象
package com.example.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class AddService extends Service {
@Override
public IBinder onBind(Intent intent) {
// 从 Intent 中获取 Binder 对象,并返回
return intent.getParcelableExtra("binder");
}
}
MainActivity.java
// 在服务端的 MainActivity.java 中,实现 IAdd 接口,并创建一个 Binder 对象
package com.example.aidldemo;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private EditText etX, etY;
private Button btnAdd;
// 实现 IAdd 接口,并创建一个 Binder 对象
private IBinder mBinder = new IAdd.Stub() {
@Override
public int add(int x, int y) throws RemoteException {
return x + y;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etX = findViewById(R.id.et_add_x);
etY = findViewById(R.id.et_add_y);
btnAdd = findViewById(R.id.btn_add);
// 启动服务,并传递 Binder 对象
Intent intent = new Intent(this, AddService.class);
intent.putExtra("binder", mBinder);
startService(intent);
// 设置按钮点击事件,获取输入的两个数,并调用 Binder 对象的 add 方法,打印结果
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
int x = Integer.parseInt(etX.getText().toString());
int y = Integer.parseInt(etY.getText().toString());
int result = mBinder.add(x, y);
Log.d(TAG, "result: " + result);
} catch (NumberFormatException | RemoteException e) {
e.printStackTrace();
}
}
});
}
}
客服端
// 在客户端的 MainActivity.java 中,绑定服务,并获取 Binder 对象,调用其 add 方法,打印结果
package com.example.client;
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.util.Log;
import com.example.aidldemo.IAdd;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private IAdd mIAdd; // 声明一个 IAdd 接口的变量
// 创建一个 ServiceConnection 对象,用于绑定服务和获取 Binder 对象
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 将 IBinder 对象转换为 IAdd 接口,并赋值给 mIAdd 变量
mIAdd = IAdd.Stub.asInterface(service);
try {
// 调用 mIAdd 的 add 方法,并打印结果
int result = mIAdd.add(3, 5);
Log.d(TAG, "result: " + result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建一个 Intent 对象,指定要绑定的服务的包名和类名
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.aidldemo", "com.example.aidldemo.AddService"));
// 绑定服务,并传递 ServiceConnection 对象
bindService(intent, mConnection, BIND_AUTO_CREATE);
}
}
AIDL 自动生成代码
这是一个 IAdd.java 文件,是由 IAdd.aidl 文件自动生成的。它主要包含以下几个部分:
IAdd 接口:定义了一个 add 方法,用于接收两个 int 类型的参数,返回一个 int 类型的结果。
Stub 类:继承自 Binder 类,实现了 IAdd 接口,用于在服务端创建 Binder 对象,并处理来自客户端的事务请求。
Proxy 类:实现了 IAdd 接口,用于在客户端创建代理对象,并通过 transact 方法向服务端发送事务请求,并接收回复。
TRANSACTION_add 常量:表示 add 方法的事务码,用于区分不同的方法调用。
DESCRIPTOR 常量:表示接口描述符,用于标识不同的接口类型。
// 这是一个 IAdd.java 文件,是由 IAdd.aidl 文件自动生成的
package com.example.aidldemo;
// 导入一些必要的类
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
// 定义一个 IAdd 接口,继承自 IInterface 接口
public interface IAdd extends IInterface {
// 定义一个常量,表示接口描述符
public static final String DESCRIPTOR = "com.example.aidldemo.IAdd";
// 定义一个常量,表示 add 方法的事务码
public static final int TRANSACTION_add = IBinder.FIRST_CALL_TRANSACTION + 0;
// 声明一个 add 方法,抛出 RemoteException 异常
public int add(int x, int y) throws RemoteException;
// 定义一个 Stub 类,继承自 Binder 类,实现 IAdd 接口
public static abstract class Stub extends Binder implements IAdd {
// 定义一个常量,表示接口描述符
private static final String DESCRIPTOR = "com.example.aidldemo.IAdd";
// 构造方法,将接口描述符设置为 Binder 的标识符
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
// 静态方法,将 IBinder 对象转换为 IAdd 接口,并返回
public static IAdd asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
// 获取 Binder 对象关联的接口代理对象
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IAdd))) {
// 如果是本地 Binder 对象,则直接返回接口对象
return ((IAdd) iin);
}
// 否则返回一个代理对象,用于远程调用
return new Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
// 如果是获取接口描述符的事务,则将描述符写入回复数据中,并返回 true
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
// 如果是 add 方法的事务,则先检查数据包是否有足够的数据,并读取接口描述符
data.enforceInterface(DESCRIPTOR);
// 从数据包中读取两个 int 类型的参数
int x = data.readInt();
int y = data.readInt();
// 调用 add 方法,并获取结果
int result = this.add(x, y);
// 将结果写入回复数据中,并标记成功
reply.writeNoException();
reply.writeInt(result);
return true;
}
default: {
// 其他情况交给父类处理
return super.onTransact(code, data, reply, flags);
}
}
}
// 定义一个 Proxy 类,实现 IAdd 接口
private static class Proxy implements IAdd {
private IBinder mRemote; // 声明一个 IBinder 对象的变量
Proxy(IBinder remote) {
mRemote = remote; // 构造方法,传入一个 IBinder 对象,并赋值给变量
}
@Override
public IBinder asBinder() {
return mRemote; // 返回 IBinder 对象
}
public String getInterfaceDescriptor() {
return DESCRIPTOR; // 返回接口描述符
}
@Override
public int add(int x, int y) throws RemoteException {
Parcel data = Parcel.obtain(); // 获取一个空的数据包对象
Parcel reply = Parcel.obtain(); // 获取一个空的回复数据包对象
int result; // 声明一个 int 类型的变量,用于存储结果
try {
data.writeInterfaceToken(DESCRIPTOR); // 将接口描述符写入数据包中
data.writeInt(x); // 将第一个参数写入数据包中
data.writeInt(y); // 将第二个参数写入数据包中
mRemote.transact(Stub.TRANSACTION_add, data, reply, 0); // 调用 transact 方法,将数据包发送给远程 Binder 对象,并等待回复
reply.readException(); // 从回复数据包中读取异常信息,如果有异常则抛出
result = reply.readInt(); // 从回复数据包中读取结果,并赋值给变量
} finally {
data.recycle(); // 回收数据包对象
reply.recycle(); // 回收回复数据包对象
}
return result; // 返回结果
}
}
}
}
调用逻辑
- 客户端:客户端是指需要使用服务端提供的功能或数据的应用组件,例如一个 Activity。客户端需要做以下几件事:
- 创建一个 Intent 对象,指定要绑定的服务的包名和类名。
- 创建一个 ServiceConnection 对象,用于绑定服务和获取 Binder 对象。
- 调用 bindService 方法,传入 Intent 对象和 ServiceConnection 对象,绑定服务。
- 在 ServiceConnection 对象的 onServiceConnected 方法中,将 IBinder 对象转换为 IAdd 接口,并赋值给一个变量。
- 调用 IAdd 接口的 add 方法,并获取结果。
- 生成代码:生成代码是指由 aidl 工具根据 IAdd.aidl 文件自动生成的 IAdd.java 文件。生成代码主要包含以下几个部分:
- IAdd 接口:定义了一个 add 方法,用于接收两个 int 类型的参数,返回一个 int 类型的结果。
- Stub 类:继承自 Binder 类,实现了 IAdd 接口,用于在服务端创建 Binder 对象,并处理来自客户端的事务请求。
- Proxy 类:实现了 IAdd 接口,用于在客户端创建代理对象,并通过 transact 方法向服务端发送事务请求,并接收回复。
- TRANSACTION_add 常量:表示 add 方法的事务码,用于区分不同的方法调用。
- DESCRIPTOR 常量:表示接口描述符,用于标识不同的接口类型。
- Stub 类:Stub 类是生成代码中的一个内部抽象类,它继承自 Binder 类,并实现了 IAdd 接口。Stub 类主要做以下几件事:
- 构造方法:将接口描述符设置为 Binder 的标识符。
- 静态方法 asInterface :将 IBinder 对象转换为 IAdd 接口,并返回。如果是本地 Binder 对象,则直接返回接口对象;否则返回一个 Proxy 对象。
- 实例方法 asBinder :返回自身对象。
- 实例方法 onTransact :重写 Binder 的 onTransact 方法,用于处理来自客户端的事务请求。根据不同的事务码,从数据包中读取参数,并调用相应的接口方法,并将结果写入回复数据包中。
- Proxy 类:Proxy 类是生成代码中的一个内部类,它实现了 IAdd 接口。Proxy 类主要做以下几件事:
- 构造方法:传入一个 IBinder 对象,并赋值给一个变量。
- 实例方法 asBinder :返回 IBinder 对象。
- 实例方法 getInterfaceDescriptor :返回接口描述符。
- 实例方法 add :实现 IAdd 接口的 add 方法。创建一个空的数据包对象和回复数据包对象,并将接口描述符和参数写入数据包中。调用 IBinder 对象的 transact 方法,将数据包发送给远程 Binder 对象,并等待回复。从回复数据包中读取异常信息和结果,并返回结果。
源: 与必应的对话, 2023/6/20
(1) Generating C++ Binder Interfaces with aidl-cpp - Google Open Source. https://android.googlesource.com/platform/system/tools/aidl/+/brillo-m10-dev/docs/aidl-cpp.md.
(2) Android Interface Definition Language (AIDL). https://developer.android.com/guide/components/aidl.
(3) What is " Stub " and “AIDL” for in java? - Stack Overflow. https://stackoverflow.com/questions/10648280/what-is-stub-and-aidl-for-in-java.