Binder学习总结——应用篇

0 阅读3分钟

一、Binder 与AIDL的关系

Binder是跨进程通信IPC的一种方式,AIDL(Android interface definition Language) 即 Android 接口定义语言)是使用Binder的封装的一个模板、一种简单方法。

二、Binder调用流程

implements com.monir.demoserver.IRemote, 以add 方法为例。

image.png

  1. AIDL文件编译后会生成一个对应的java文件,里面是个继承了AIDL接口的proxy.
  2. Client调用函数如add时,调用proxy的add方法,会通过Remote.transact 函数名和参数。
  3. BinderProxy 调用transactNative,底层会调用底层Binder驱动,最终会调用Binder的execTransact,里面会调用onTransact。而Stub类重写了onTransact()方法,里面会调用服务端的add方法。

参考:Binder通信机制与AIDL的基本使用

            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;
        }

运行结果如下:

image.png

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端接口同时执行,但客户端会卡住。

image.png

用oneway修饰接口:client1和Process2都调用了server端时,service端接口顺序执行。

image.png