美文网首页
Android-Binder机制的理解

Android-Binder机制的理解

作者: 九号锅炉 | 来源:发表于2019-04-26 11:33 被阅读0次

    安卓中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机制的理解

          本文链接:https://www.haomeiwen.com/subject/ldtawqtx.html