Binder
- 在安卓使用Binder实现进程间通信需要做哪些工作
- 如何模糊跨进程调用与进程内调用?
- 如何使用AIDL
如何利用Binder实现进程间通信
我们先看下Binder调用大致原理,这是Binder调用的标准调用过程,我们下面的代码将逐渐从不标准过程转成标准过程:
首先,我们 android studio 新建两个工程(两个 moudle 也可以,这里目的为创建两个运行在不同进程的app),一个Server,一个Client,而后,在Server中,我们新建一个java类Stub(类名无所谓),继承android.os.Binder,之后重写onTransact 方法,此处注意,onTransact方法的四个参数:
- code:方法标识符,因为Client端对Server端的所有调用都会走到Server端的这个方法,所以理所应当Client端应该传递一个参数过来用以表示要调用哪个方法,注意这个int类型的标识必须介于 FIRST_CALL_TRANSACTION 和 LAST_CALL_TRANSACTION之间,所以我们给方法分配code的时候最好使用FIRST_CALL_TRANSACTION+n 这种方式
- data :Client传递过来的序列化数据包,Parcel类型
- reply: 如果Client端调用时需要返回值,Server通过这个对象将返回值传递回去,同样Parcel类型
- flag 用来区分这个调用是普通调用还是单边调用,普通调用时,Client端线程会阻塞,直到从Server端接收到返回值(所以如果Client端是主线程调用,其调用的Server端不宜做耗时操作,这会让造成Client的ANR),若flag==IBinder.FLAG_ONEWAY,则这次调用是单边调用,Client在传出数据后会立即执行下一段代码,此时两端异步执行,单边调用时函数返回值必须为void (也就是异步调用必须舍弃返回值,要返回值就必须阻塞等待)
有以上,Server端的功能就已经可以实现,但在两端通信时,为了两端Binder匹配,我们还需要在Server端做一次验证,用到data.enforceInterface(DESCRIPTOR)这个方法,DESCRIPTOR是Binder描述符,Binder Server和Client之间将通过这个描述符做验证,要想通过验证Binder通信的两端DESCRIPTOR必须相同,这也是为什么我们在使用AIDL帮助我们生成Binder代码的时候,必须把AIDL放在相同的包名下,因为SDK会根据包名为我们生成对应的DESCRIPTOR字符串,这里我们手写Binder,只需要保证两端相同就好了,包名字符串不是必须的
下面为Server端完整代码
public class Stub extends android.os.Binder {
//用于标识调用的Binder
private static final java.lang.String DESCRIPTOR = "MyBinder";
//方法标识,这里我们准备两个方法,一个无参,一个有参
private static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
private static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case TRANSACTION_method0: {
//Store or read an IBinder interface token
//验证Binder标识
data.enforceInterface(DESCRIPTOR);
//调用实现方法
this.method0();
reply.writeNoException();
return true;
}
case TRANSACTION_method1: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
//按写入的顺序读取数据
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.method1(_arg0, _arg1);
reply.writeNoException();
//向Client写回返回值
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//Server端真正实现业务的两个方法
public void method0() throws RemoteException {
Log.e("Server", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method0");
}
public int method1(int a, int b) throws RemoteException {
Log.e("Server", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method1" + " " + a + " " + b);
return a + b;
}
}
应用间要实现Binder通信必须要用Service来完成,想象客户端要怎样才能知道服务端的Binder地址并向其写入数据,一种是客户端通过一个Binder地址总管查询,通过键名查找到对应的Binder服务,这种方式就是有名Binder,这个总管类就是ServiceManager,应用进程获取系统服务就是通过查询这个Binder总管实现的,比如应用进程启动进入java层后就会去查找AMS的客户端,就是通过ServiceManager来查找的,但作为应用进程,是不能向ServiceManager注册有名Binder的,所以我们的客户端也没法通过ServiceManager查询到对应的Binder服务端,但应用进程间依然是可以获取到对方的Binder服务端的,Binder并不一定要注册到ServiceManager才能被获取到,这种Binder的获取方式就是通过已经获取到的Binder传递Binder,也就是说如果有某个有名Binder服务它提供了传递Binder的方法,那么我们就可以通过这个Binder服务来传递我们的匿名Binder,正好,AMS作为一个有名Binder提供了这个功能,其对Binder传递被封装到了Service组件当中,我们可以通过Service.onBind 来返回我们要传递的匿名Binder客户端,而在Activity.bindService中获取到这个Binder:
@Override
public IBinder onBind(Intent intent) {
return new Stub();
}
在Client端Activity 中bindService,我们来看Activity的代码:
//定义常量
//注意两个工程中对应的标识符必须相同
static final String DESCRIPTOR = "MyBinder";
//方法标识
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.xxx.server", "com.xxx.server.ServerService"));
boolean b = bindService(intent, conn, BIND_AUTO_CREATE);
Log.e("Client", " "+b);
}
bindService 的第二个参数,ServiceConnnection,在这个回调中我们取得IBinder对象,这个对象是Server端在Client中的一个代理对象
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
//这个IBinder对象iBinder,可以调用c++层Binder代理并最终通过Binder驱动传递数据
Parcel _data0 = Parcel.obtain();//申请传递参数的Parcel对象(从Parcel池中取出)
Parcel _reply0 = Parcel.obtain();//申请接收返回值的Parcel对象,相当于数据载体
Parcel _data1 = Parcel.obtain();
Parcel _reply1 = Parcel.obtain();
try {
//调用第一个方法
//写入Binder标识,以便服务端验证
_data0.writeInterfaceToken(DESCRIPTOR);
//传入方法标识,以便服务端知道我们要调用哪个方法,注意最后一个参数,就是上面提到的 //flag,如果我们传入IBinder.FLAG_ONEWAY,则这次调用为单边调用,这个方法会立即返 //回,不会等服务端方法返回
iBinder.transact(TRANSACTION_method0, _data0, _reply0, 0);
_reply0.readException();
//调用第二个方法
_data1.writeInterfaceToken(DESCRIPTOR);
//按顺序写入参数
_data1.writeInt(1);
_data1.writeInt(2);
//从下面这行代码开始本线程会阻塞,直到服务端进程中调用的方法完成计算返回后这个线程继 //续运行,计算的返回值放入_reply1中
iBinder.transact(TRANSACTION_method1, _data1, _reply1, 0);
_reply1.readException();
int i = _reply1.readInt();//从reply中读取返回值,这里我们就得到了服务端计算后的结果
} catch (RemoteException e) {
e.printStackTrace();
} finally {
//回收Parcel
_data0.recycle();
_reply0.recycle();
_data1.recycle();
_reply1.recycle();
}
Log.e("Client", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method0");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
通过以上代码,我们就可以实现跨进程间的方法调用
我们可以对onServiceConnected方法里的代码做一定封装,使用Proxy类封装对IBinder的操作,使得调用的时候更方便
public class Proxy {
static final String DESCRIPTOR = "MyBinder";
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
public void method0() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_method0, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
public int method1(int a, int b) 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(a);
_data.writeInt(b);
mRemote.transact(TRANSACTION_method1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
此时ServiceConnection对象可以更改为
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
Proxy proxy = new Proxy(iBinder);
try {
proxy.method0();
int i = proxy.method1(1,2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
如何模糊跨进程调用与进程内调用?
分析问题
- onServiceConnected(ComponentName name, IBinder iBinder) 这个方法传递的IBinder接口对象是什么,在同进程与不同进程时有什么不一样?
- 如果通过bindService 传递过来的IBinder对象是同进程的,那我们还需要使用IBinder.transact传递数据吗?要知道的Binder的使用需要层层调用并最终在内核空间进行一次数据复制
- 如果我们想跨进程的时候创建 Proxy 类包裹 IBinder 对象的操作,同进程的时候直接强转 IBinder 对象为我们定义的对象或接口,不通过代理类直接使用其方法,应该怎么做?
对第一个问题,我们在 asInterface 里面打印一下传进来的IBinder实例是什么类型,发现如果是远程调用,传给我们的 iBinder 是 BinderProxy 类型,他在native层会对应一个C++的BpBinder,BpBinder 最终会通过Binder驱动跟Server端通信。如果是本地调用,打印出的类型为Stub,说明本地调用时,onServiceConnected传过来的就是我们在Service的onBinde方法返回的Stub对象本身。在这个基础上,为了让远程调用(通过我们新建Proxy封装BinderProxy对象)和本地调用(直接调用继承自Binder的Stub对象)统一,我们让Proxy和Stub实现相同的接口,再实现一个静态方法,根据传递的IBinder对象返回一个对象,这个对象实现我们定义的接口,在进程内调用时,这个对象就是Stub类及其子类对象,当跨进程调用时,这个对象就是Proxy实例,由于要考虑两种情况,我们就需要在这个静态方法中作出判断,判断传递的IBinder对象是本地对象还是远程对象,再根据判断决定直接强转为我们定义的接口返回,或生成Proxy对象强转返回。此时我们需要将两份文件合并。
怎样区分IBinder对象的具体类型,我们可以通过IBinder的queryLocalInterface(DESCRIPTOR)方法,得到IInterface对象,判断是否为null:
//定义静态方法根据传递的IBinder对象返回实现相同接口的不同对象
public static IMyInterface asInterface(IBinder iBinder){
if ((iBinder == null)) {
return null;
}
IInterface iin = iBinder.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IMyInterface))) {
return ((IMyInterface)iin );
}
return new Proxy(iBinder);
}
若为Stub则返回值就是Stub实例本身,让我们看一下Binder.queryLocalInterface的实现
/**
* Use information supplied to attachInterface() to return the
* associated IInterface if it matches the requested
* descriptor.
*/
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
若为代理类BindProxy则为空,我们看下其实现:
public IInterface queryLocalInterface(String descriptor) {
return null;
}
将传进来的descriptor与mDescriptor比较,若相同,说明这是进程内调用,返回mOwner,这个mOwner和mDescriptor是需要Binder调用attachInterface赋值,所以我们在Stub构造方法里调用这个方法,又因为这个方法需要的参数为IInterface,所以我们让Stub实现这个接口,最后由于我们已经将Proxy和Stub文件合并,在我们需要给别的进程绑定我们的iBinder时,需要把这个文件添加到对应的应用里,我们这个就需要把Stub两个方法的实现抽离出来,具体的实现放在我们的业务代码里,让Server端Stub子类去实现
public interface IMyInterface {
//定义两个方法
void method0() throws RemoteException;
int method1(int a, int b) throws RemoteException;
//用于标示调用的Binder
static final java.lang.String DESCRIPTOR = "MyBinder";
//用于标识调用的方法
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
//定义静态方法根据传递的IBinder对象返回实现相同接口的不同对象
public static IMyInterface asInterface(IBinder iBinder){
if ((iBinder == null)) {
return null;
}
IInterface iin = iBinder.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IMyInterface))) {
return ((IMyInterface)iin );
}
return new Proxy(iBinder);
}
public abstract class Stub extends android.os.Binder implements IMyInterface,IInterface {
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
@Override
public 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 TRANSACTION_method0: {
data.enforceInterface(DESCRIPTOR);//Store or read an IBinder interface token
this.method0(); //
reply.writeNoException();
return true;
}
case TRANSACTION_method1: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt(); //按写入的顺序读取数据
int _arg1;
_arg1 = data.readInt();
int _result = this.method1(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result); //向Client写回返回值
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
public class Proxy implements IMyInterface {
static final String DESCRIPTOR = "MyBinder";
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public void method0() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_method0, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int method1(int a, int b) 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(a);
_data.writeInt(b);
mRemote.transact(TRANSACTION_method1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
将如上文件放在需要通信的两个工程里,就可以实现Binder通信,如果同进程调用,就会直接使用返回的对象而不通过Binder机制。
可以看出调用总是由客户端发起,由服务端运算结束。
怎么实现进程间互调?
Binder被设计为CS模式,其本身是不支持服务端主动调客户端的,但我们可以有一些曲线救国的方式,观察Parcel这个类,它是支持传递IBinder和IInterface接口的,我们可以在Client调用Server时将Client端定义的Binder服务传递至Server,Server端拿到这个Binder地址,就可以在服务端也创建一个BinderProxy,就可以像Client端调用它一样调用Client端的Binder,这时双方角色互换,应用进程启动时就是这样把自己的ApplicationThread 通过AMS传递给system_server进程的,所以system_server在应用启动并主动绑定AMS后就可以通过ApplicationThreadProxy来远程调用应用的方法从而管理应用了。
观察Parcel传递IInterface这个方法
public final void writeStrongInterface(IInterface val) {
writeStrongBinder(val == null ? null : val.asBinder());
}
实际还是传递的 IBinder,这里回去看我们实现的Binder通信的代码,我们只让 Stub 实现了 IInterface ,然而我们希望在传递 IInterface 时不用去区分是 Stub 还是 Proxy,我们让 Proxy 也实现IInterface这个接口,现在Stub和Proxy都实现我们定义的IMyInterface和系统提供的IInterface这两个接口,所以我们让IMyInterface直接继承IInterface就好了,现在Proxy需要重写IInterface的asBinder,返回mRemote变量就好了。至此,我们完成一份完整的 Binder 封装代码,这份代码和我们编写 IMyInterface.aidl 文件编译后编译器为我们生成的 java 文件是一样的,下面是使用Android Studio编写aidl文件后编译生成的java文件:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /home/end/AndroidStudioProjects/Demo/demo2/src/main/aidl/com/xjh/end/demo2/IMyAidlInterface.aidl
*/
package com.xjh.demo;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.xjh.demo.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.xjh.demo.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
//可以用于区分此次调用是进程内还是进程间,因为进程内调用的,Stub 子类对象也就是
//服务端实例 的构造函数被调用过程中将 DESCRIPTOR 保存为了自己的成员变量,所以调用
//obj.queryLocalInterface(DESCRIPTOR)得到的结果不为空(实例实现是返回Binder子类也 //是Stub子类对象本身),如果是代理Binder端,之前的代码可以看出BinderProxy类重新的方法 //直接返回就是null,这就可以区分当前调用是
//进程内还是进程间了
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.xjh.demo.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.xjh.demo.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.xjh.demo.IMyAidlInterface))) {
return ((com.xjh.demo.IMyAidlInterface) iin);
//若是进程内调用,直接强转返回就可以
}
//若为进程间调用,需要用一个代理类封装,这个代理类封装了往Binder发消息的代码,使得调用
//想进程内调用一样方便
return new com.xjh.demo.IMyAidlInterface.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_method0: {
data.enforceInterface(DESCRIPTOR);
//调用子类实现
this.method0();
reply.writeNoException();
return true;
}
case TRANSACTION_method1: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
//调用子类实现
int _result = this.method1(_arg0, _arg1);
reply.writeNoException();
//向客户端写入返回值
//android.os.Parcel data 和 reply 都只是数据的载体,至于数据具体是怎么
//通过Binder发送的,先不关心
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//aidl生成的文件把Stub作为IMyAidlInterface接口的内部类,又把Proxy作为Stub的内部类,但是 // 是不是内部并没有关系,这两个内部类都是静态的,放在哪里都一样,只是对外隐藏了这个代理类,使得
//使用者只能使用Stub.asInterface 来返回这个对象而不能直接使用这个类创建实例,使得对Binder
//进程内和进程间的访问都被封装成接口访问,模糊两者的区别,体现了更好的封装。
private static class Proxy implements com.xjh.demo.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
//就是BinderProxy类的实例
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
//Proxy类的作用就是封装了远程调用时想Binder写入数据的操作,
//这里仍然以android.os.Parcel类对象作为数据载体
@Override
public void method0() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
//写入描述符,以通过服务端验证
_data.writeInterfaceToken(DESCRIPTOR);
//将数据写入Binder
//数据的写入最终就是写入Binder驱动,数据写入内核空间,而
//由于Binder的机制,有部分内核空间和用户空间的逻辑地址映射到了同一块物理地址,
//所以服务端进程不需要再把数据复制到用户空间,
//这也是Binder进行进程间通信效率高的原因之一,
//只经过了一次数据拷贝,而像Socket,则需要经过两次拷贝,
//先从A进程将数据写入内核空间,由于进程间在内核空间共享逻辑地址,
//所以B进程在内核空间也可以访问到这个数据,但由于没有像Binder一样做内存映射,
//进程的内核空间和用户空间的逻辑地址不共享,
//在B进程的用户空间就访问不到这个数据,
//所以还要从内核空间再将数据拷贝到B进程的用户空间,完成一次跨进程数据传递
mRemote.transact(Stub.TRANSACTION_method0, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int method1(int a, int b) 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(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_method1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
//这就是Stub未实现需要子类自己实现的方法,子类实现这两个方法实现服务端逻辑
public void method0() throws android.os.RemoteException;
public int method1(int a, int b) throws android.os.RemoteException;
}
以上代码算是我们分析AIDL的原理分析,我们定义的AIDL文件最终会生成以上代码,可以看出数据在客户端写入和在服务端读出都是用了Parcel作为载体,这个类作用很强大,其还内部封装了对Serializable接口数据的Binder传递方法,我们会在另一篇文章中解读这个类。
我们定义AIDL接口以后,只需要在Server端继承Stub并实现我们定义的接口方法,在客户端将传递过来的IBinder对象用 asInterface 方法封装,然后我们就可以像使用本地对象一样调用远程对象了。
最终实现的效果,进程内调用时,就是直接调用实现类的方法(method0,method1),跨进程调用时,客户端通过Proxy类往代理Binder写值,在服务端进程里,再取出这些值,根据这些数据再去调用对应的 实现类的方法(method0,method1),在服务端,方法的调用是在Binder线程里进行的,远程调用的每次调用在服务端都是在Binder线程里进行的,这些Binder线程由Binder线程池管理,也就是说如果是远程调用,method0,method1是运行在Binder线程里的,那么如果想让我们的调用在Server端主线程执行,我们需要在Server端主线程创建Handler来把消息通过handler再转给主线程来实现,这是AMS管理应用组件生命周期的方式,这也是Android另一个进程间通信方式Messager的原理,其实就是封装了Binder和Handler,Binder跨进程发过来的消息立即转到Handler,再用Handler把消息从Binder线程转到指定线程,在指定线程中处理,这里我们即使不看代码也应该可以判断,Message的消息发送都是单边调用,消息一旦发出不会等待调用结果返回。
要注意的是,如果我们是在App Process的UI thread 里面双边调用远程对象,显然和调用本地对象一样,这个调用不能是耗时操作,UI thread 会等待远端方法返回后再继续运行。
这里贴一下以上java代码对应的AIDL文件,SDK就是根据这个文件生成了以上一大串java代码:
// IMyAidlInterface.aidl
package com.xjh.demo;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
void method0();
int method1(int a, int b);
}
如何使用AIDL
AIDL接口支持哪些数据类型
- Java 基本数据类型
- String和CharSequence
- Parcelable
- List 和 Map(泛型类型必须是以上类型)
- AIDL接口
aidl的使用需要我们声明对应的aidl接口,编译时SDK会根据aidl文件生成相应的java代码,其功能就是封装Binder的操作,使跨进程调用和本地调用一样方便,上面已经贴出传递基本类型的AIDL代码,而对于对象的传递我们需要用到Parcelable接口,而在aidl接口中的方法,其 Parcelable 类型的参数不管是不是属于同一个包都需要 import,aidl 接口也是,除此之外,形参还需要指定 in | out | inout 类型,基本类型默认且只能是in类型,out类型指Binder服务端不读取从客户端传过来的数据,而直接创建空对象,在这次调用的末尾再把这个对象再传递回客户端,inout就是接收客户端传过来的数据,生成对象,调用过后再把这个对象传递回去。
所有需要在Binder中传递的 Parcelable 的实现类都需要创建与其名相同的 aidl 文件,这个文件中不必写接口和方法,如我要实现一个Book 类,实现Parcelable接口:
package com.xxx.xxx;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable{
public String name;
public int price;
protected Book(Parcel in) {
name = in.readString();
price = in.readInt();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
}
// Book.aidl
package com.xxx.xxx;
parcelable Book;
以上可知我们实现 Parcelable 这个接口,主要的工作就是把我们想要传递的对象包含的数据写入 Parcel 以及从Parcel中取出数据生成对应的对象,其实还是把对象拆成基本类型,再在另一端再次生成对象,Binder底层传递的还是基本类型,其实对于安卓的另一个序列化接口Serializable,Parcel也就会将其反射取出数据装入byte数组,然后在另一端取出来反射再生成对象,Binder底层传递的还是基本类型,我们将用一篇文章讲解Parcel这个类。
复习下这张图: