本文主要是介绍Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
系列文章
Android 进程间通信(一) – Android 多进程模式
Android 进程间通信(二) – 理解 Binder 的机制
Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务
上一章,已经学习了 Binder 的通信原理,这里再通过 AIDL 了,再来捋一遍,并自己写个 Binder。
如果你对 AIDL 不熟悉,可以参考这篇文章 AIDL使用详解及进程回调
一. AIDL 基本使用
这里也是用上面的代码,首先是任务类 TaskInfo,需要继承 Parcelable 接口,让as 帮你实现方法即可,如下:
public class TaskInfo implements Parcelable {public int id;public String url;public int progress;public TaskInfo() {}protected TaskInfo(Parcel in) {id = in.readInt();url = in.readString();progress = in.readInt();}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(id);dest.writeString(url);dest.writeInt(progress);}@Overridepublic int describeContents() {return 0;}public static final Creator<TaskInfo> CREATOR = new Creator<TaskInfo>() {@Overridepublic TaskInfo createFromParcel(Parcel in) {return new TaskInfo(in);}@Overridepublic TaskInfo[] newArray(int size) {return new TaskInfo[size];}};@Overridepublic String toString() {return "TaskInfo{" +"id=" + id +", url='" + url + '\'' +", progress=" + progress +'}';}
}
然后是 TaskInfo.aidl
//注意这个 TaskInfo 必须是在 com.example.ipcdemo 下,不如会提示找不到。
parcelable TaskInfo;
IRemoteService.aidl :
//记得导入 TaskInfo.aidl 的包
import com.example.ipcdemo.TaskInfo;
interface IRemoteService {//两数之和int add(int num1,int num2);//添加一个任务TaskInfo addTask(in TaskInfo info);
}
然后 build 一下,就会发现,在 build 下生成了 IRemoteService.java
接着看这个 IRemoteService 的 java ,代码有点长,可以略过:
public interface IRemoteService extends android.os.IInterface
{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.example.ipcdemo.IRemoteService{private static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.IRemoteService";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.example.ipcdemo.IRemoteService interface,* generating a proxy if needed.*/public static com.example.ipcdemo.IRemoteService asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.ipcdemo.IRemoteService))) {return ((com.example.ipcdemo.IRemoteService)iin);}return new com.example.ipcdemo.IRemoteService.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_add:{data.enforceInterface(descriptor);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.add(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}case TRANSACTION_addTask:{data.enforceInterface(descriptor);com.example.ipcdemo.TaskInfo _arg0;if ((0!=data.readInt())) {_arg0 = com.example.ipcdemo.TaskInfo.CREATOR.createFromParcel(data);}else {_arg0 = null;}com.example.ipcdemo.TaskInfo _result = this.addTask(_arg0);reply.writeNoException();if ((_result!=null)) {reply.writeInt(1);_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);}else {reply.writeInt(0);}return true;}default:{return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.example.ipcdemo.IRemoteService{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 int add(int num1, int num2) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(num1);_data.writeInt(num2);boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().add(num1, num2);}_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}//添加一个任务@Override public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();com.example.ipcdemo.TaskInfo _result;try {_data.writeInterfaceToken(DESCRIPTOR);if ((info!=null)) {_data.writeInt(1);info.writeToParcel(_data, 0);}else {_data.writeInt(0);}boolean _status = mRemote.transact(Stub.TRANSACTION_addTask, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().addTask(info);}_reply.readException();if ((0!=_reply.readInt())) {_result = com.example.ipcdemo.TaskInfo.CREATOR.createFromParcel(_reply);}else {_result = null;}}finally {_reply.recycle();_data.recycle();}return _result;}public static com.example.ipcdemo.IRemoteService sDefaultImpl;}static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);public static boolean setDefaultImpl(com.example.ipcdemo.IRemoteService impl) {if (Stub.Proxy.sDefaultImpl == null && impl != null) {Stub.Proxy.sDefaultImpl = impl;return true;}return false;}public static com.example.ipcdemo.IRemoteService getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}/** Default implementation for IRemoteService. */public static class Default implements com.example.ipcdemo.IRemoteService{//两数之和@Override public int add(int num1, int num2) throws android.os.RemoteException{return 0;}//添加一个任务@Override public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException{return null;}@Overridepublic android.os.IBinder asBinder() {return null;}}//两数之和public int add(int num1, int num2) throws android.os.RemoteException;//添加一个任务public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException;
在解释之前,先了解一些概念:
- IInterface : AIDL 文件必须继承的接口,它只有一个方法 IBinder asBinder() ,实现它的类,代表的是能够跨进程传输 Binder 对象,或者 Binder 代理对象,比如上面的代码中,返回this,表示 Stub 这个内部类是具有跨进程的作用的。
- IBinder : 实现这个接口的对象具备跨进程书传输的能力,在跨进程数据流经驱动时,驱动会识别 Binder 类型的数据,从而自动完成不同进程 Binder 本地对象以及 Binder 代理对象的转换。
二. 分析Binder 流程
首先,我们看看我们在服务中时怎么构建 Binder 服务的:
//AIDL 的服务,IRemoteService.Stub mBinder = new IRemoteService.Stub() {@Overridepublic int add(int num1, int num2) throws RemoteException {/*** 这里为具体的实现方法,比如直接返回两数之和*/Log.d(TAG, "zsr add: 接收到客户端传递的两个数字: "+num1+" "+num2);return (num1 + num2);}@Overridepublic TaskInfo addTask(TaskInfo info) throws RemoteException {Log.d("zsr", "收到客户端的信息: "+info);info.id = 0;info.progress = 50;// mHandler.sendEmptyMessage(1);// mTaskInfo = info;return info;}};
可以看到,使用的是 IRemoteService 的子类 Stub ,那我们从这个类开始分析好了。
DESCRIPTOR
看到在 Stub 子类中,实现了一个标记字符串,用包名加类型表示:
private static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.IRemoteService";
然后在它的构造方法中,注册当前的 Binder 和 字符串:
public Stub() {this.attachInterface(this, DESCRIPTOR);}
后面需要从 ServerManager 中拿到binder 时,就需要这个字符串了,比如:
queryLocalInterface(DESCRIPTOR)
asInterface
将服务端的 Binder 对象转换成客户端所有的 AIDL 对象;比如:
IRemoteService mBinder = IRemoteService.Stub.asInterface(service);
这个转换是分进程的,如果同个进程,则直接返回自身即可,如果是跨进程,则需要使用它的代理类,去转换了。从代码也可以看出来:
public static com.example.ipcdemo.IRemoteService asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.ipcdemo.IRemoteService))) {return ((com.example.ipcdemo.IRemoteService)iin);}return new com.example.ipcdemo.IRemoteService.Stub.Proxy(obj);}
asBinder
从上面的介绍中已经知晓,返回的对象,具备跨进程的能力,这里返回 this,表示 Stub。
onTransact
该方法运行在服务端的Binder线程池中,数据的写入和结果读取都在这。
当客户端发起跨进程请求时,远程请求会通过底层封装后,交由次方法来处理;从 code 中,可以知道当前执行的是哪个方法,比如我们定义的,add() 和 addTask() 它们的 index 为:
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
这样,我们就可以通过 code 去拿到对应的方法:
继续看 onTransact(int code, Parcel data, Parcel reply, int flags),它的参数有可以序列换和反序列化的 Parcel 参数,data 和 reply;
我们可以从 data 中取出参数(如果方法有参数的话),注意有顺序,然后把结构写入 replay ,这样当你的方法有返回值的时候,replay 返回的就是你需要的。
注意,onTransact 需要 返回 true,返回false 则表示请求失败。
IRemoteService#Proxy
从上面到说,当跨进程调用时,asInterface 返回的是代理类的实力,它也是实现 IRemoteService 接口,在Android 进程间通信(二) – 理解 Binder 的机制 我们知道,Binder 的代理模式,其实就是当 A 访问 B 的 object 方法,B 不会把的 object 给到 A,而是给它一个具体相同方法的代理类,然后A 通过这个代理把参数传递给 B,B 计算之后,把结果再传递给 A;从而实现跨进程的作用。
而它也确实是这么做的:
首先通过 _data 把参数都写进去,接着使用mRemote.transact 把参数传递给服务,它胡调用 onTransact 方法,然后通过线程池,把结果写入 reply ,最后再返回给 Binder,完成了跨进程的动作。
这样,我们就完成了 AIDL 的分析
三. 自定义 AIDL 服务
从上面看,AIDL 中,系统生成的逻辑比较乱,且所有类都集成在一起,但我们理清楚之后,还是比较容易理解的。那么这里,我们自己来写一个,而不是使用系统生成的。
首先,定义一个接口,让它集成 IInterface,并添加 add 和 addTask 方法,且定义好方法的 id:
public interface IRemote extends IInterface {static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.aidl.IRemote";static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addTask = (IBinder.FIRST_CALL_TRANSACTION + 1);//两数之和public int add(int num1, int num2) throws RemoteException;//添加一个任务public TaskInfo addTask(TaskInfo info) throws RemoteException;}
然后创建一个具备跨进程的实现类 IRemoteLmpl ,集成 Binder 和实现 IRemote 接口:
public class IRemoteImpl extends Binder implements IRemote {@Overridepublic int add(int num1, int num2) throws RemoteException {return 0;}@Overridepublic TaskInfo addTask(TaskInfo info) throws RemoteException {return null;}@Overridepublic IBinder asBinder() {return this;}
}
接着,我们应该在的构造方法中,把binder 和 字符串描述注册到服务中:
public IRemoteImpl() {this.attachInterface(this,DESCRIPTOR);}
实现供客户端调用的服务端转换的 AIDL 对象的方法 asInterface:
public static IRemote asInterface(IBinder obj){if (obj == null) {return null;}if (obj instanceof IRemote){return (IRemote) obj;}return new Proxy(obj);}
其中 Proxy 代理类,我们后面再写。
接着,编写 IRemote 的具体实现方法 onTransact :
@Overrideprotected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {//通过 code 对应的方法switch (code) {case TRANSACTION_add:data.enforceInterface(DESCRIPTOR);//接受到参数数据int num1 = data.readInt();int num2 = data.readInt();int result = this.add(num1, num2);reply.writeNoException();//把结果返回到返回参数中reply.writeInt(result);return true;case TRANSACTION_addTask:data.enforceInterface(DESCRIPTOR);TaskInfo info = null;if (data.readInt() != 0) {//通过反序列化拿到数据info = TaskInfo.CREATOR.createFromParcel(data);}TaskInfo resultInfo = this.addTask(info);reply.writeNoException();if ((resultInfo!=null)) {reply.writeInt(1);info.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);}else {reply.writeInt(0);}return true;default:return super.onTransact(code, data, reply, flags);}}
无法就是一些数据的转换,没啥问题,接着,实现代理类,方便跨进程调用:
/*** 跨进程的代理类*/private static class Proxy implements IRemote {private IBinder mRemote;Proxy(IBinder iBinder) {mRemote = iBinder;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic IBinder asBinder() {return mRemote;}@Overridepublic int add(int num1, int num2) throws RemoteException {Parcel data = Parcel.obtain();Parcel replay = Parcel.obtain();int reslut;try {data.writeInterfaceToken(DESCRIPTOR);data.writeInt(num1);data.writeInt(num2);/*** 可以看到,当跨进程被调用的时候,只是把调用方的参数,给自身的方法运行,然后再把结果返回回去*/mRemote.transact(TRANSACTION_add, data, replay, 0);replay.readException();reslut = replay.readInt();} finally {data.recycle();replay.recycle();}return reslut;}@Overridepublic TaskInfo addTask(TaskInfo info) throws RemoteException {Parcel data = Parcel.obtain();Parcel replay = Parcel.obtain();TaskInfo result;try {data.writeInterfaceToken(DESCRIPTOR);if ((info != null)) {//用数据 0,1 ,来区分是否写入成功data.writeInt(1);info.writeToParcel(data, 0);} else {data.writeInt(0);}mRemote.transact(TRANSACTION_addTask, data, replay, 0);replay.readException();if ((0 != replay.readInt())) {result = TaskInfo.CREATOR.createFromParcel(replay);} else {result = null;}} finally {data.recycle();replay.recycle();}return result;}}
这样,我们的代码就写完了
接着修改给其他调用的 RemoteService 的 AIDL 服务:
//AIDL 的服务,IRemoteImpl mBinder = new IRemoteImpl() {@Overridepublic int add(int num1, int num2) throws RemoteException {/*** 这里为具体的实现方法,比如直接返回两数之和*/Log.d(TAG, "zsr add: 接收到客户端传递的两个数字: "+num1+" "+num2);return (num1 + num2);}@Overridepublic TaskInfo addTask(TaskInfo info) throws RemoteException {Log.d("zsr", "收到客户端的信息: "+info);info.id = 0;info.progress = 50;// mHandler.sendEmptyMessage(1);// mTaskInfo = info;return info;}};
然后,把这两个方法复制到 客户端:
绑定服务端服务:
//绑定 AIDL 服务
intent.setClassName("com.example.ipcdemo","com.example.ipcdemo.service.RemoteService");
class RemoteService implements ServiceConnection{@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// mBinder = IRemoteService.Stub.asInterface(service);mBinder = IRemoteImpl.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {}}
然后在点击事件中,调用 AIDL 方法,拿到数据:
public void testAidl(View view) {try {if (mBinder != null) {TaskInfo info = new TaskInfo();info.url = "www.google.com";TaskInfo taskInfo = mBinder.addTask(info);int num = mBinder.add(2,3);Log.d(TAG, "zsr testAidl: 获取到服务端数据: "+taskInfo+" "+num);}} catch (RemoteException e) {e.printStackTrace();}}
打印如下;
这样,我们不通过 AIDL 也实现了跨进程通信了。
工程代码:https://gitee.com/zhengshaorui/IpcDemo
这篇关于Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!