Android-Binder机制的理解

安卓中Binder机制是一种跨进程通信的方式,在日常应用开发中四大组件底层通信机制、Activity传递对象以及AIDL的使用等,都涉及到Binder机制。既然Binder是一种跨进程通信方式,那么首先就要知道什么是跨进程通信,为什么需要跨进程通信。(PS:计算机领域和自然科学领域的学习方法有很大的差异,自然科学的很多原理是自然固有的原理,只不过被人类总结归纳出来并用于发展科学技术。而计算机以及其他工程技术,即便是再高深复杂的技术手段,均不是凭空冒出来的,都是在不断解决问题中提炼优化出来的,是对某一类问题的解决方法的一种抽象。因此只要沿着这种技术发展的脉络和历史不断深究,层层递进,就能描绘出整个技术发展的轮廓。要记住的是,技术不会凭空出现,都是为了解决某一类的问题而提出来的)。

跨进程通信

在操作系统中不同进程之间是无法相互访问的,通过虚拟内存空间的方式,每个进程认为自己是独占整个内存空间。但是如果一个进程想要访问另外一个进程的数据应该如何实现?传统的Linux跨进程通信方式有Socket,共享内存,信号,管道,消息队列。(PS:这里面需要提前介绍一下内核空间和用户空间。操作系统将每个进程的虚拟内存空间分为用户空间和内核空间。内核空间涉及到访问底层硬件设备以及一些操作系统核心操作等,为了防止进程随意访问造成对操作系统的破坏,进程在正常情况下是无法访问内核空间的。但是有时候进程需要访问存储硬件或者访问网络,需要访问内核空间怎么办?这就需要系统调用(System call)。用户空间访问内核空间的唯一方式就是系统调用。程序通过系统调用访问内核空间称为进入内核态。)那么传统Linux的IPC是如何传递数据的呢。通常的做法是发送进程将数据放在内存缓冲区,然后调用系统调用进入内核态,在内核空间开辟内核缓冲区。然后调用copy_from_user将用户空间的内存缓冲区数据拷贝到内核空间的内核缓冲区。同样接受进程在用户空间开辟缓冲区,内核程序调用copy_to_user将内核缓冲区的数据拷贝到用户空间的内存缓冲区。

传统IPC方式有两个缺点:
1需要拷贝两次,性能低下。虽然共享内存只要拷贝一次,但是共享内存控制困难,需要处理复杂的同步问题。
2由于接受进程不知道接受的数据有多大,因此就会分配过多空间。

Binder跨进程通信

那么相比于传统IPC方式,Binder跨进程通信方式有什么优点呢?下面结合Binder通信原理进行介绍。Binder机制和网络一样都是基于Client/Server架构的。Binder机制中有Client/Server/ServiceManager/Binder驱动四个角色。Client和Server就是跨进程通信的双方,而ServiceManager就是安卓SystemServer中负责管理各个service的。首先Server会在SM中注册一个实体Binder,同时生成一个代理BinderProxy.Client通信时会让SM帮忙去注册表里查找是否存在这个Server的Binder,如果存在,会返回这个代理。Client想要调用服务端的方法时,其实不是调用Server里实体binder的方法,而是通过调用这个代理,代理会把数据返回到Server中实体Binder进行处理,然后再返回给Client.而整个通信过程涉及到系统调用或者与底层交互的,都是通过binder驱动来实现的。

同时Binder通过采用内存映射的方式传递数据,因此只需要拷贝一次数据就可以。具体做法是:将内核缓冲区与接受进程的数据接受缓冲区建立映射,当数据从发送进程的内存缓冲区拷贝到内核缓冲区时,接受进程的数据接受缓冲区就会立即变化。

基于AIDL源码进一步分析Binder机制

上面概括性的介绍了Binder跨进程通信的原理。下面结合安卓Aidl进行详细的讲解
AIDL(Android Interface Define Language 安卓接口定义语言)其实就是方便用户建立Binder机制的一种模板。用户完全可以不需要AIDL自己生成对应的代码。

定义一个接口继承IInterface.表明Server端具备什么能力供Client调用。

public interface ICalculate extends IInterface {
    void add(int a, int b) throws RemoteException;
}

利用ICalculate.AIDL生成ICalculate.java文件。下面具体分析。

public interface ICalculate extends android.os.IInterface {
    //Stub类继承Binder表明该类是一个实体Binder,同时继承了ICalculate接口表明具有该接口具
    //备的能力.
    public static abstract class Stub extends android.os.Binder implements ICalculate {
        // Binder唯一标识。用于Server注册和Client查找
        private static final java.lang.String DESCRIPTOR = "com.example.ICalculate";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        // 调用bindService后onServiceConneted中需要调用这个函数来获得这个Binder.代
        // 码中通过调用queryLocalInterface查找是否存在本地Binder,如果存在,表明Client
        // 和server在同一个进程,可以直接返回这个对象。如果不存在则需要创建一个代理 
        // Binder对象。
        public static ICalculate asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof ICalculate))) {
                return ((ICalculate) iin);
            }
            return new ICalculate.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
         //根据代理发送过来的Code来决定具体执行哪一个方法。每个方法都有一个唯一的Code.
        @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_add: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    //调用本地add方法得到计算结果,写入reply返回。
                    int _result = this.add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements ICalculate {
            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;
            }
            //代理Binder中实现了和实体Binder相同能力的接口。Client得到代理Binder后,就可以
            //调用add函数。如之前所描述的,这个Proxy中的add函数不是实体binder的add函      
            //数,而是将数据发送给Server,让server调用实体Binder的add函数来执行,并把结果 
            //返回。
            @Override
            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);
                    //将a,b通过parcel序列化成data
                    _data.writeInt(a);
                    _data.writeInt(b);
                    //transact中会调用onTransact方法将计算结果写入_reply返回。
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                   //得到计算结果。
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    public int add(int a, int b) throws android.os.RemoteException;
}

Binder的优点

1.性能。这一点上面阐述过了,Binder通过内存映射只需要拷贝一次数据。
2.安全性。在Linux中每个用户都有一个唯一的UserId.而安卓是单用户,每个应用拥有一个Uid.只有Uid相同的进程才可以进行数据共享和通信。这是安卓安全权限的一种方式。在Binder中会通过识别Uid来限制其他进程的访问,保证了应用的安全性。

你可能感兴趣的:(Android-Binder机制的理解)