一、Binder 与AIDL的关系
Binder是跨进程通信IPC的一种方式,AIDL(Android interface definition Language) 即 Android 接口定义语言)是使用Binder的封装的一个模板、一种简单方法。
二、Binder调用流程
implements com.monir.demoserver.IRemote, 以add 方法为例。
- AIDL文件编译后会生成一个对应的java文件,里面是个继承了AIDL接口的proxy.
- Client调用函数如add时,调用proxy的add方法,会通过Remote.transact 函数名和参数。
- BinderProxy 调用transactNative,底层会调用底层Binder驱动,最终会调用Binder的execTransact,里面会调用onTransact。而Stub类重写了onTransact()方法,里面会调用服务端的add方法。
public int add(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_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
mRemote.transact
BinderProxy.transact
transactNative
android_util_Binder.android_os_BinderProxy_transact
( //gBinderProxyOffsets.mObject中保存的是new BpBinder(handle)对象
IBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject);)
target->transact // BPBinder.transact
IPCThreadState::transact
IPCThreadState::waitForResponse
IPCThreadState::talkWithDriver
参考链接:
Binder大总结_binderproxy.transact-CSDN博客 : 侧重函数跟读
三、参数in,out,inout, oneway作用
1. in, out, inout
参数类型默认是in。 int,String 等基本类型不能修饰为out或inout等类型。
当为in时,参数能传递至server端;但是server端修改值后不会影响client原来的值。
为out时,参数不能传递至server端,下面的例子中server打印出来为[0,0];
当为inout时,才能两边互传。
int test(in int a, in byte[] inArray,out byte[] outArray, inout byte[] inoutArray);
private void test() {
byte[] inArray = new byte[]{1, 2};
byte[] outArray = new byte[]{8, 9};
byte[] inoutArray = new byte[]{8, 9};
try {
int a = 5;
Log.d(TAG, "client ------------origin a: " + a);
Log.d(TAG, "origin inArray: " + Arrays.toString(inArray));
Log.d(TAG, "origin outArray: " + Arrays.toString(outArray));
Log.d(TAG, "origin inoutArray: " + Arrays.toString(inoutArray));
iRemote.test(5, inArray, outArray, inoutArray);
Log.d(TAG, "client---------after a: " + a);
Log.d(TAG, "after inArray: " + Arrays.toString(inArray));
Log.d(TAG, "after outArray: " + Arrays.toString(outArray));
Log.d(TAG, "after inoutArray: " + Arrays.toString(inoutArray));
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public int test(int a, byte[] inArray, byte[] outArray, byte[] inoutArray) throws RemoteException {
Log.d(TAG, "server-----origin a: " + a);
Log.d(TAG, "origin inArray: " + Arrays.toString(inArray));
Log.d(TAG, "origin outArray: " + Arrays.toString(outArray));
Log.d(TAG, "origin inoutArray: " + Arrays.toString(inoutArray));
a = 0;
inArray[0] = 0;
outArray[0] = 10;
inoutArray[0] = 0;
Log.d(TAG, "server----after a: " + a);
Log.d(TAG, "after inArray: " + Arrays.toString(inArray));
Log.d(TAG, "after outArray: " + Arrays.toString(outArray));
Log.d(TAG, "after inoutArray: " + Arrays.toString(inoutArray));
return 0;
}
运行结果如下:
2. oneway修饰与否的区别
oneway 表示异步调用。修饰的接口不能有返回值,且参数不能包含out和inout修饰。默认为同步调用,会导致调用端阻塞。
不用oneway修饰时,多进程能同时调用server,受线程限制DEFAULT_MAX_BINDER_THREADS15个,加上 Binder 主线程,共 16 个可并发处理事务。system_server 显式调到31个。多的到Binder 驱动层排队。
用oneway修饰时,多进程调用同一个接口,Server 端走异步队列串行分发顺序执行。
实践中:监控 Binder 线程池:用 Perfetto 看 client_dur/ server_dur/ dispatch_dur,dispatch_dur飙高基本就是线程池排队的信号。
oneway效果对比
下面server端sleep 4s。分别在Client1和Process2进程调用。
未用oneway修饰接口:client1和Process2都调用了server端时,service端接口同时执行,但客户端会卡住。
用oneway修饰接口:client1和Process2都调用了server端时,service端接口顺序执行。