AIDL背景
在Android平台上,一个进程通常无法访问另一个进程的内存,所以想要跨进程访问的话,需要将要传递的数据分解为系统可以支持和识别的基本单元,有序的经过进程的边界。因为这个操作十分繁琐,所以Android使用AIDL来解决这个问题。AIDL就是用于生成两个进程间进行进程间通信的(IPC)代码,面向开发简化这个过程。
注:只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。无论如何,在实现 AIDL 之前,请您务必理解绑定服务。
定义AIDL接口
- 创建一个.aidl文件。
- 实现接口,此接口具有一个名为 stub 的内部抽象类,用于扩展 Binder 类并实现 aild 定义的接口。
- 向客户端公开接口,实现 Service 并重写 onBind() 返回给 Stub 类的实现。
创建aidl文件
默认情况下,aidl支持的数据类型有:
- 基本数据类型( int, long, char, boolean, double等等)
- String 和 CharSequence
- List : 每个元素都必须被 aidl 支持,只支持ArrayList
- Map : 每个元素都必须被 aidl 支持,支持支HashMap
- Parcelable : 所有实现了 Parcelable 接口的对象
- aidl : 所有 aidl 接口本身也可以在 aidl 文件中使用
一个 aidl 文件例子 :
// IStudentManager.aidl
package com.seaicelin.aidl;
import com.seaicelin.aidl.Student;
interface IStudentManager {
List<Student> getStudentList();
void addStudent(in Student student);
}
Android Studio会自动生成一个 aidl 文件相对应的 java 接口文件,它包含一个名为 stub 的抽象类和他的代理类 proxy。
package com.seaicelin.aidl;
public interface IStudentManager extends android.os.IInterface {
//Local-side IPC implementation stub class.
public static abstract class Stub extends android.os.Binder implements com.seaicelin.aidl.IStudentManager {
private static final java.lang.String DESCRIPTOR = "com.seaicelin.aidl.IStudentManager";
//Construct the stub at attach it to the interface.
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
//Cast an IBinder object into an com.seaicelin.aidl.IStudentManager
//interface,generating a proxy if needed.
public static com.seaicelin.aidl.IStudentManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.seaicelin.aidl.IStudentManager))) {
return ((com.seaicelin.aidl.IStudentManager) iin);
}
return new com.seaicelin.aidl.IStudentManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getStudentList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.seaicelin.aidl.Student> _result = this.getStudentList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addStudent: {
data.enforceInterface(DESCRIPTOR);
com.seaicelin.aidl.Student _arg0;
if ((0 != data.readInt())) {
_arg0 = com.seaicelin.aidl.Student.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addStudent(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.seaicelin.aidl.IStudentManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.util.List<com.seaicelin.aidl.Student> getStudentList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.seaicelin.aidl.Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.seaicelin.aidl.Student.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addStudent(com.seaicelin.aidl.Student student) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((student != null)) {
_data.writeInt(1);
student.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.seaicelin.aidl.Student> getStudentList() throws android.os.RemoteException;
public void addStudent(com.seaicelin.aidl.Student student) throws android.os.RemoteException;
}
远程服务端service实现接口
服务端实现上文中提到的 stub 抽象类,实现 aidl 接口。这里的 mBinder 是 Stub 的一个 实例,用于定义服务的 RPC 接口;在下一步中,将向客户端公开该实例,以便客户端能与服务端进行交互。
<service
android:name=".StudentManagerService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
public class StudentManagerService extends Service {
private static final String TAG = "[StudentManagerService]";
private CopyOnWriteArrayList<Student> mStudentList = new CopyOnWriteArrayList<>();
public StudentManagerService() {
}
@Override
public void onCreate(){
super.onCreate();
mStudentList.add(new Student(1, "seaicelin1"));
mStudentList.add(new Student(2, "seaicelin2"));
mStudentList.add(new Student(3, "seaicelin3"));
}
//实现aidl生成的java文件的Stub抽象类并实现aidl方法
//aidl方法在Binder线程池中执行,多个客户端同时连接时,需要处理线程同步
//CopyOnWriteArrayList支持并发读写,支持线程同步
private Binder mBinder = new IStudentManager.Stub(){
@Override
public List<Student> getStudentList() throws RemoteException {
return mStudentList;
}
@Override
public void addStudent(Student student) throws RemoteException {
if(mStudentList.contains(student))
return;
mStudentList.add(student);
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
客户端实现
在 service 内部实现接口后,需要向客户端公开该接口,以便客户端绑定。服务端需要创建一个 service 并实现 onBind() ,返回一个实例,这个实例实现了上文提到的 Stub 抽象类。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = "[MainActivity]";
private Button mBtnService;
private IStudentManager mStudentManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnService = (Button) findViewById(R.id.button);
mBtnService.setOnClickListener(this);
}
//创建 ServiceConnection 对象
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mStudentManager = IStudentManager.Stub.asInterface(service);
try {
List<Student> list = mStudentManager.getStudentList();
for(int i = 0; i < list.size(); i++){
Log.e(TAG, "query student list " + list.get(i).toString());
}
Student student = new Student(4, "seaicelin4");
mStudentManager.addStudent(student);
List<Student> newList = mStudentManager.getStudentList();
for(int i = 0; i < newList.size(); i++){
Log.e(TAG, "after add student, query student list " + newList.get(i).toString());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
public void onClick(View v) {
Intent intent = new Intent(this, StudentManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
startService(intent);
Log.e(TAG, "onClick");
}
@Override
protected void onDestroy(){
unbindService(mConnection);
super.onDestroy();
}
}
由此,客户端创建完毕,查询服务端的有多少个学生,然后增加一个学生,最后再次打印服务端的学生数。我们可以通过 log 来看看是否已经能够正常使用。
03-25 09:17:09.117 31597-31597/com.seaicelin.aidl E/[MainActivity]: onClick
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 1 name = seaicelin1
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 2 name = seaicelin2
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 3 name = seaicelin3
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 1 name = seaicelin1
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 2 name = seaicelin2
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 3 name = seaicelin3
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 4 name = seaicelin4
到这里,已经成功使用 aidl 进行跨进程通信,aidl 的基本使用介绍完毕。
总结调用 IPC 方法
用谷歌官方文档作为总结吧,调用类必须执行以下步骤,才能调用使用 AIDL 定义的远程接口:
- 在项目 src/ 目录中加入 .aidl 文件。
- 声明一个 IBinder 接口实例(基于 AIDL 生成)。
- 实现 ServiceConnection。
- 调用 Context.bindService(),以传入您的 ServiceConnection 实现。
- 在您的 onServiceConnected() 实现中,您将收到一个 IBinder 实例(名为 service)。调用 YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为 YourInterface 类型。
- 调用您在接口上定义的方法。您应该始终捕获 DeadObjectException 异常,它们是在连接中断时引发的;这将是远程方法引发的唯一异常。
- 如需断开连接,请使用您的接口实例调用 Context.unbindService()。