Android学习之旅-使用 AIDL 实现进程间通信02[艺术
本篇文章将着重于分析 AIDL 是如何通过 Binder 来实现进程间通信的,当然只是分析它的上层应用,不然 Binder 让我现在的能力去看绝对是云里雾里,没多大价值,倒不如退而求其次,毕竟学习需要一步一步的来,而且前面的两篇文章也是这周学习完 IPC 机制后利用空余时间来一次实践总结,我计划在每周学习完一个专题的内容后都去总结一遍,一步一步的提示自身的开发能力。
好了,通过上篇文章我们知道 AIDL 其实就是帮助我们来实现进程间通信的一个工具,新建完 AIDL 文件重新 rebuild 之后在 AS 的 build 文件夹下会自动生成 java 代码,其实就是协助我们进行跨进程通信的,你可以理解为 AIDL 是在 binder 的基础上进行了一次封装,方便了我们的开发者,👌下面看代码:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/tengfei/AndroidStudy/Android-HotYong/01_AndroidCrossProcess/client/src/main/aidl/com/example/tengfei/aidl/IBookManager.aidl
*/
package com.example.tengfei.aidl;
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.tengfei.aidl.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.example.tengfei.aidl.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.tengfei.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.tengfei.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.tengfei.aidl.IBookManager))) {
return ((com.example.tengfei.aidl.IBookManager) iin);
}
return new com.example.tengfei.aidl.IBookManager.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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<com.example.tengfei.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.example.tengfei.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.tengfei.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.tengfei.aidl.IBookManager {
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.example.tengfei.aidl.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.tengfei.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.tengfei.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.example.tengfei.aidl.Book book) 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 ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.example.tengfei.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.tengfei.aidl.Book book) throws android.os.RemoteException;
}
代码很多,但其实整体结构上还是很清晰的,首先是一个接口 IBookManager 继承了 IInterface ,在接口内部定义了两个方法 getBookList() 和 addBook(com.example.tengfei.aidl.Book book) 只有继承了 IInterface 的接口才可以通过 binder 传递,在其内部定义了一个静态抽象类 Stub 继承自 Binder 并且实现了 IBookManager 接口,所以说 IBookManager.java
这个类的核心就是 Stub 类,下面让我们来具体看一下 Stub 的内部结构。
- DESCRIPTOR : 给继承自 binder 的 stub 类定义一个唯一的标记,一般用当前的类名来表示,在 Stub 的构造方法中 调用 binder 的 attachInterface 并将当前定义的 DESCRIPTOR 传递进参数中,其作用是为了将特定接口与 binder 关联
- TRANSACTION_getBookList : 一个 int 整型值用来标记 getBookList 方法
- TRANSACTION_addBook : 一个 int 整型值用来标记 addBook 方法
- asInterface(android.os.IBinder obj) : 将服务端的 binder 对象转换为客户端所需要的 AIDL 接口类型的对象,如果是客户端与服务端处于同一个进程,那么返回的就是服务端的 Stub 对象,否则返回的就是代理类 Proxy 对象
- asBinder : 返回当前的 binder 对象
- onTransact : 运行在服务端 binder 线程池中,调用远程服务的 transact 过程其实最终就是回调 onTransact 方法,通过它参数中的 code 可以确定调用的是哪一个方法,从 data 中取出调用的目标方法所需要的参数,当方法执行完毕后向reply中写入返回值,值得注意的是如果 onTransact 方法的返回值是 false 那么客户端的请求会失败
- Proxy : Proxy 是一个代理类,它实现了 IBookManager 接口,在其内部重写了 getBookList 和 addBook 方法,这两方法运行在客户端中,由我们开发者自己去调用,让我们简单分析下这个类的具体实现,看代码:
private static class Proxy implements com.example.tengfei.aidl.IBookManager {
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.example.tengfei.aidl.Book> getBookList() throws android.os.RemoteException {
//创建输入型Parcel对象
android.os.Parcel _data = android.os.Parcel.obtain();
//创建输出型Parcel对象
android.os.Parcel _reply = android.os.Parcel.obtain();
//返回值对象
java.util.List<com.example.tengfei.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.tengfei.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.example.tengfei.aidl.Book book) throws android.os.RemoteException {
//创建输入型Parcel对象
android.os.Parcel _data = android.os.Parcel.obtain();
//创建输出型Parcel对象
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
首先它是一个静态内部类,我们不管这些,其实都可以分开来写的,将我们的注意力重点放在 getBookList 和 addBook 方法上,首先是 getBookList 方法,分别通过 Parcel.obtain() 构造出 两个Parcel 对象 _data 和 _reply 一个用于输入数据,一个用于输出数据,Parcel是一个容器,它可以包含数据或者是对象引用,并且能够用于Binder的传输。调用 Parcel 的 writeInterfaceToken 用于说明给当前的数据加上中介数据用来标识data是给含有DESCRIPTOR标志的Binder接口的参数,这句话的意思是说我 data 的数据是给定义了 DESCRIPTOR 标记的接口来用的,这本示例中对应着 Stub 类,紧接着调用 IBinder 的 transact 方法来执行远程RPC过程,调用 transact 方法最终回调服务端 binder线程池终的 onTransact 方法,至于 addBook 与 getBookList 差不多,就不讲啦。。。
总结:
1.定义一个接口继承自 IInterface,在接口中定义客户端与服务端通信的方法,只有继承了 IInterface 的接口才能在binder中传递
2.定义一个抽象类继承自 Binder 并且实现了 IInterface 类型的接口,在它的构造方法中为此类定义唯一的标记,在 asInterface 方法中将服务端的 IBinder 对象返回为客户端所需要的 AIDL 接口类型的对象,但是是有区别的,如果客户端与服务端处于同一个进程,那么返回的是服务端的 Stub 对象,否则返回的就是 Stub.Proxy 代理类对象
3.代理类 Proxy 实现了 IInterface类型的接口,并重写相关方法,这些方法运行在客户端,由我们开发人员自己调用,并最终调用 IBinder 的 transact 方法执行远程 RPC 过程,在 transact 方法中传入指定执行方法的标记以及输入输出型Parcel对象和 flag ,flag 一般定义为 0
4.执行 transact 方法最终会回调服务端binder线程池中的 onTransact 方法,在 onTransact 方法中通过传递过来的 code 可以判断出具体要执行的方法,在其内部执行写入和读取数据的操作的相关逻辑
5.客户端在发起远程请求时,客户端会被挂起知道服务端返回数据给客户端才会被唤醒
最后用一张流程图来描述下使用 AIDL 是如何实现进程间通信的,如下:
AIDL执行进程间通信的方式
我上面写的不一定都是对的,仅仅是我个人学习过程中的思考以及摘录,在以后的学习过程中如果我发现了错误我会及时改正,也请开发前辈们如果发现了本文的错误及时指出,大家一起进步😄😄😄😄
参考资料
1、Android开发艺术探索
2、对Binder的浅显分析及AIDL的使用
3、Android Parcel对象详解