Binder在应用层最典型的应用是AIDL,而学习AIDL的调用流程能帮助我们了解Binder在Framework层的工作。这篇文章基于AIDL调用过程进行简单梳理。
为了打印log方便,我这边给到的是自定义AIDL的例子,代码如下:
一、两端公共的Binder方法接口:
IOperation接口
public interface IOperation extends IInterface {
//Binder描述符
static final String DESCRIPTOR = "com.zht.aidltest.IOperation";
//方法标记id,用于区分不同的方法调用
static final int TRANSACTION_getSum = IBinder.FIRST_CALL_TRANSACTION + 0;
// 定义了一个供跨进程调用的方法
public double getSum(double first, double second) throws RemoteException;
}
IInterface是Binder接口基类。此处接口属于定于规范,它只实现了一个asBinder()的方法。那么想要进行AIDL调用的接口,必须要定义在接口中,且该接口继承IInterface。
二、server端:
IOperationStub Binder调用的服务端执行类
public class IOperationStub extends Binder implements IOperation{
public static final String TAG = "ZHT server";
public IOperationStub() {
/**
* 将一个IInterface类型的接口实例与Binder关联起来,DESCRIPTOR相当于一个标识。当该方法被调用后,
* 可通过queryLocalInterface(DESCRIPTOR)获取与这个标识相应的接口实例
*/
this.attachInterface(this, DESCRIPTOR);
}
@Override
public IBinder asBinder() {
return this;
}
/**
* 该方法运行在服务端,当客户端发起请求,会进入到该方法进行处理
* code:用于标识客户端请求调用的目标方法(即在IOperation中定义的方法标识符)
* data:当请求的目标方法含有参数时,该参数封装了请求的参数
* reply:当请求需要返回结果时,该参数封装了处理结果
*/
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
// 通过code区分不同的方法调用
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_getSum:
Log.d(TAG,"onTransact: TRANSACTION_getSum");
data.enforceInterface(DESCRIPTOR);
// 通过data获取请求的参数
double first = data.readDouble();
double second = data.readDouble();
Log.d(TAG,"onTransact: TRANSACTION_getSum first: "+first+"; second: "+second);
double result = this.getSum(first, second);
Log.d(TAG,"onTransact: TRANSACTION_getSum result: "+result);
reply.writeNoException();
// 将结果写入到reply中
reply.writeDouble(result);
return true;
default:
break;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public double getSum(double fisrt, double second) throws RemoteException {
Log.d(TAG,"IOperationStub: getSum");
return 0;
}
private static class Proxy implements IOperation {
private IBinder mRemote;
Proxy(IBinder mRemote) {
this.mRemote = mRemote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public double getSum(double fisrt, double second)
throws RemoteException {
Log.d(TAG,"Proxy: getSum");
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
double result = 0;
try {
data.writeInterfaceToken(DESCRIPTOR);
// 将请求的参数写入到data中
data.writeDouble(fisrt);
data.writeDouble(second);
// 调用transact()方法,发起跨进程请求,当前进程挂起
mRemote.transact(TRANSACTION_getSum, data, reply, 0);
Log.d(TAG,"Proxy: mRemote.transact");
// 从reply中获取返回的结果
reply.readException();
result = reply.readDouble();
} catch (Exception e) {
} finally {
data.recycle();
reply.recycle();
}
return result;
}
}
}
RemoteService Server端真正被客户端需要的服务
public class RemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("ZHT server","calling uid: "+MyBinder.getCallingUid()+"calling pid: "+MyBinder.getCallingPid());
return new MyBinder();
}
class MyBinder extends IOperationStub {
@Override
public double getSum(double fisrt, double second) throws RemoteException {
Log.d("ZHT server","RemoteService: getSum");
return fisrt + second;
}
}
}
三、client端
IOperationProxy Binder调用的客户端执行类
public class IOperationStub extends Binder implements IOperation {
public static final String TAG = "ZHT client";
public IOperationStub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 用于将服务端的Binder对象转换成客户端需要的AIDL接口类型的对象(该过程区分进程):
* client和server处于同一进程:直接返回客户端的Stub对象
* client和server处于不同进程:返回封装好的Stub.proxy对象
*/
public static IOperation asInterface(IBinder obj) {
if (obj == null) {
return null;
}
/**
* 尝试在本地检索实现了该接口的Binder对象实例
*/
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && (iin instanceof IOperation)) {
return ((IOperation) iin);
}
/**
* 假如本地检索返回为null,需要手动生成一个proxy代理类,并在其中通过transact() 方法去处理请求
*/
return new IOperationStub.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
// 通过code区分不同的方法调用
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_getSum:
Log.d(TAG, "onTransact: TRANSACTION_getSum");
data.enforceInterface(DESCRIPTOR);
// 通过data获取请求的参数
double first = data.readDouble();
double second = data.readDouble();
Log.d(TAG, "onTransact: TRANSACTION_getSum first: " + first + "; second: " + second);
double result = this.getSum(first, second);
Log.d(TAG, "onTransact: TRANSACTION_getSum result: " + result);
reply.writeNoException();
// 将结果写入到reply中
reply.writeDouble(result);
return true;
default:
break;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public double getSum(double fisrt, double second) throws RemoteException {
Log.d(TAG, "IOperationStub: getSum");
return 0;
}
private static class Proxy implements IOperation {
private IBinder mRemote;
Proxy(IBinder mRemote) {
this.mRemote = mRemote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public double getSum(double fisrt, double second)
throws RemoteException {
Log.d(TAG, "Proxy: getSum");
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
double result = 0;
try {
data.writeInterfaceToken(DESCRIPTOR);
data.writeDouble(fisrt);
data.writeDouble(second);
mRemote.transact(TRANSACTION_getSum, data, reply, 0);
Log.d(TAG, "Proxy: mRemote.transact");
reply.readException();
result = reply.readDouble();
} catch (Exception e) {
} finally {
data.recycle();
reply.recycle();
}
return result;
}
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private IOperation mIOperation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
double result = mIOperation.getSum(1.00, 1.00);
Toast.makeText(MainActivity.this, String.valueOf(result), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
Intent intent = new Intent();
intent.setAction("com.zht.aidltest.RemoteService");
intent.setPackage("com.zht.aidltestserver");
startService(intent);
bindService(
intent,
new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIOperation = IOperationProxy.asInterface(service);
Log.d("ZHT client: ", "" + mIOperation + ";name: " + name);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
},
Context.BIND_AUTO_CREATE
);
}
}
代码非常简单,就是在client项目的MainActivity中点击按钮,调用server项目的RemoteService执行1+1的操作。
先看看执行代码之后,打印是如何走的:
12-23 16:19:00.438 7995-7995/com.zht.aidltestserver D/ZHT server: calling uid: 10064calling pid: 7995
12-23 16:19:00.705 8091-8091/? D/ZHT client:: com.zht.aidltest.IOperationStub$Proxy@dde2115;name: > ComponentInfo{com.zht.aidltestserver/com.zht.aidltest.RemoteService}
12-23 16:20:06.631 8091-8091/com.zht.aidltest D/ZHT client: Proxy: getSum
12-23 16:20:06.632 7995-8006/com.zht.aidltestserver D/ZHT server: onTransact: TRANSACTION_getSum
12-23 16:20:06.632 7995-8006/com.zht.aidltestserver D/ZHT server: onTransact: TRANSACTION_getSum first: 1.0; second: 1.0
12-23 16:20:06.632 7995-8006/com.zht.aidltestserver D/ZHT server: RemoteService: getSum
12-23 16:20:06.632 7995-8006/com.zht.aidltestserver D/ZHT server: onTransact: TRANSACTION_getSum result: 2.0
12-23 16:20:06.635 8091-8091/com.zht.aidltest D/ZHT client: Proxy: mRemote.transact
那么接下来总结下整个调用流程:
1 bindService 绑定远程RemoteService,最终通过ServiceManager获取对应服务的IBinder (startService在此不讨论,默认让RemoteService先起来)
2 IOperationStub.asInterface(service);
asInterface中 obj.queryLocalInterface(DESCRIPTOR);
DESCRIPTOR是我们在IOperation定义的Binder标识符,标识一个IInterface类型的接口实例与Binder的关联。
尝试在本地检索实现了该接口的Binder对象实例,如果客户端和服务端属于同一进程,直接返回客户端的Stub对象,即直接return queryLocalInterface查询的对象,若处于不同进程返回封装好的Stub.proxy对象。
显然此处查询结果是null, return new IOperationStub.Proxy(obj);
3 asInterface结束后, IOperation mIOperation 就被赋值了,它此时代表的就是远程的RemoteService,可以直接调用其方法实现毫无跨进程感知的调用。但是具体怎么做的呢?还得接着往下走
4 点击按钮执行mIOperation.getSum(1.00, 1.00); 因为之前asInterface 返回的是new IOperationStub.Proxy(obj); 那么对应的就是执行Proxy对象中的getSum方法
5 Proxy对象中的getSum方法 有两个Parcel对象,Parcel本身就是可以用于进程间通信的对象,data 用于向server发送数据,reply用于获取server传回的数据。
data写入接口标识DESCRIPTOR 与对应的getSum的两个参数,最终通过ServiceManager获取到的IBinder,执行transact(TRANSACTION_getSum, data, reply, 0); 发起跨进程请求,此处,如果函数定义的是非oneway的,会挂起等待服务端返回结果,如果是oneway的就不会挂起等结果了,TRANSACTION_getSum是函数标识
6 Server端IOperationStub执行onTransact进行客户端请求的处理,通过data获取请求的参数,调用服务端对应的服务方法执行,并将结果写入到reply中。
7 客户端从reply中获取服务端返回的结果。
最后总结一张不成熟的小流程图:
从应用层角度理解,Binder的设计让客户端调用远程函数就像调用本地函数一样,它本身屏蔽了底层所有实现细节。如果应用层开发,了解到这,会用也就够了,但是如果想了解实现机制,那还需要继续往下看。