Binder 复习笔记

Binder 的解剖过程

Posted by Roger on January 3, 2017

关于 Binder 的一些知识点

Binder 作为 Android 底层最重要的 IPC 的方式,其重要性不言而喻,而它所覆盖的知识点又非常的复杂繁琐。记录一下关于 Binder 的一些知识点和要点,以便将来复习方便。

  1. Binder 的作用是什么?

    在 Android 启动的时候,Zygote 进程孵化出第一个子进程叫 SystemServer,很多的系统服务都是运行在这个进程的线程中,而我们的各个应用都运行在自己独立的进程中,当应用需要与系统服务进行交互的时候,就需要用到进程间通信,Binder 就是在 Android 中实现的进程通信方式。

  2. Binder 机制是如何实现的?

    Binder 主要由四个主要部分构成: Client , Service , ServiceManager , Binder驱动程序。

    顾名思义,Client 就是客户端,发起访问的一方,Serivce 就是服务端,被调用的一方, ServiceManager 类似于一个电话簿,维护了一份所有 Service 的列表,而 Binder 驱动程序则是整个 Binder 机制的核心,它是一段运行在内核空间的代码,通过 “/dev/binder” 这个文件在内核空间和用户空间来回搬数据,实现进程间的通信。

    在 Android Framewrok 中 Binder 的架构如下图所示(图片来自于Link):

    image

    客户端需要调用远程服务的时候,会初始化一个连接,并 block 住自己,等待服务端返回。在服务端,通过线程池的方式来响应请求。如上图所示,Client 通过 Proxy 来完成与 Binder Driver 进行的交互。Process B 是系统服务进程,在这个进程里面维护着多个 binder Thread,直到达到设置的线程上限。客户端主要和 Proxy 交互,服务端主要和 Stub 交互, Proxy 和 Stub 可以理解为一个相同的接口,定义了客户端和服务端相互调用的相同的接口。

    从 Framework 角度出发 Binder 的机制大概就是如此,如果要深入到 Native 层的话可以参考 gityuan 的博客,比较深入的说明了 Binder 内核的机制Link

  3. AIDL 原理解析

    利用 AIDL 实现 IPC ,原理还是利用 Framework Binder 的架构。写好的 AIDL 文件会被自动编译成一个 java 文件。例如我们写了一个如下的 AIDL 文件:

         package com.roger.aidl;
         interface mInterface{
             void invokTest();
         }
    

    编译后在 gen 目录会生成一个 java 类:

 /*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\Romworkspace\\APP SDK\\testworkspace\\AidlDemo_client\\src\\com\\roger\\aidl\\mInterface.aidl
 */
package com.roger.aidl;

public interface mInterface extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.roger.aidl.mInterface {
        private static final java.lang.String DESCRIPTOR = "com.roger.aidl.mInterface";

        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.roger.aidl.mInterface interface,
         * generating a proxy if needed.
         */
        public static com.roger.aidl.mInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.roger.aidl.mInterface))) {
                return ((com.roger.aidl.mInterface) iin);
            }
            return new com.roger.aidl.mInterface.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_invokTest: {
                data.enforceInterface(DESCRIPTOR);
                this.invokTest();
                reply.writeNoException();
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.roger.aidl.mInterface {
            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 void invokTest() 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(Stub.TRANSACTION_invokTest, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_invokTest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void invokTest() throws android.os.RemoteException;
}

这个 AIDL 文件客户端和服务端都会有相同的一份,为的是在通信的时候会有相同的接口回调。而明显客户端和服务端都只用到其中对应的部分。对于 Client 来说,使用到的是 Proxy 这个类。

看到 Activity 中的代码:

        public void onServiceConnected(ComponentName className, IBinder service) {
            Log("connect service");
            mService = mInterface.Stub.asInterface(service);
        }

在绑定服务成功后,我们首先调用到 asInterface 这个方法将一个 IBinder 类型的 service 传进来,由于 this.attachInterface(this, DESCRIPTOR) 是构造 Stub() 调用的,所以只是在 service 端能调用到, client 调用 queryLocalInterface() 会返回 null ,从而在 client 端使用的是 Proxy ,我们看到 Proxy 类:

        private static class Proxy implements com.roger.aidl.mInterface {
            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 void invokTest() 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(Stub.TRANSACTION_invokTest, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

通过获取到的 IBinder,就是我们绑定服务成功后回调获得的 IBinder,生成了一个 Proxy 类,当 Client 调用 invokTest() 方法时,通过 IBinder 的 transact() 方法将数据传输给服务端,并把线程锁住。此时,通过 Binder 驱动的层层调用,会来到 Stub 类的 onTransact 方法:

        @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_invokTest: {
                data.enforceInterface(DESCRIPTOR);
                this.invokTest();
                reply.writeNoException();
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

看到调用到了 this.invokTest(); ,其实就是我们在写服务端的时候调用的方法:

    private final mInterface.Stub mBinder = new mInterface.Stub() {
        public void invokTest() throws RemoteException {
            // TODO Auto-generated method stub
            Log.e(TAG, "remote call from client! current thread id = "
                    + Thread.currentThread().getId());
        }
    };

这样来看,一步步调用到服务端的方法实现,完成 IPC 调用。