美文网首页android技术
binder机制 源码解读

binder机制 源码解读

作者: remax1 | 来源:发表于2020-06-02 15:42 被阅读0次

    前言

    进程是操作系统分配资源的最小单位,进程的主要结构是PCB(Progress Control Block),其中就包括进程名,物理地址等。各个进程互不影响,因为有进程隔离技术。正是因为进程隔离技术,所以不同进程的资源并不共享,当A进程想去操作B进程的方法时,就需要通过进程间通信技术,也就是IPC技术来完成调用。

    图片.png
    图片来源出自:https://zhuanlan.zhihu.com/p/35519585

    Binder与传统IPC对比

    共享内存:无需拷贝,但是需要借助到Memory Map来完成用户空间 和内核空间的映射,这就涉及到同步的问题且不安全。

    Socket:需拷贝两次,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,传入效率低,开销大且不安全

    Binder:只需拷贝一次,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,借助Memory Map完成接收方和内核空间的映射,而且Binder机制需要为每个APP分配UID,在通信时需要鉴别身份。

    图片.png
    图片来源出自:https://zhuanlan.zhihu.com/p/35519585

    流程分析

    在这借助Aidl来分析整个流程。

    private void bindService() {
            Intent intent = new Intent();
            //开启服务端,也就是另一个进程的服务
            intent.setComponent(new ComponentName("com.lxxl.binder_service", "com.lxxl.binder_service.AidlService"));
            bindService(intent, connection, Context.BIND_AUTO_CREATE);
        }
    
        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(TAG, "onServiceConnected: success");
                iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, "onServiceDisconnected: success");
                iMyAidlInterface = null;
            }
        };
    

    熟悉Aidl的知道,这个ServiceConnection 就是在启动服务需要传递的参数。重点是asInterface这个方法做了啥

     public static com.lxxl.binder_service.IMyAidlInterface asInterface(android.os.IBinder obj)
        {
          if ((obj==null)) {
            return null;
          }
          android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
          if (((iin!=null)&&(iin instanceof com.lxxl.binder_service.IMyAidlInterface))) {
            return ((com.lxxl.binder_service.IMyAidlInterface)iin);
          }
          //返回了一个Proxy对象
          return new com.lxxl.binder_service.IMyAidlInterface.Stub.Proxy(obj);
        }
    

    首先,给我们返回了一个Proxy对象,这个对象用来干啥的??我们接着往下看这个Proxy类。

     private static class Proxy implements com.lxxl.binder_service.IMyAidlInterface
        {
          private android.os.IBinder mRemote;
          Proxy(android.os.IBinder remote)
          {
            mRemote = remote;
          }
    ``````省略
    
    //这个方法就是在声明Aidl时自定义的方法。
    @Override 
    public void addPerson(com.lxxl.binder_service.Person person) throws android.os.RemoteException
          {
          //这就是要需要通过binder传递的数据
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              if ((person!=null)) {
                _data.writeInt(1);
                person.writeToParcel(_data, 0);
              }
              else {
                _data.writeInt(0);
              }
              //注释①
              boolean _status = mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
              if (!_status && getDefaultImpl() != null) {
                getDefaultImpl().addPerson(person);
                return;
              }
              _reply.readException();
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
          }
    
    

    注释①:在调用一个方法时,要知道方法名和形参列表,形参列表通过_data来传递,方法名则需要传递String,不过由于服务端和客户端的AIDL文件一致,我只需要传递一个整形值,就知道是第几个方法了,效率更高。

    在调用完注释①后,会调用Binder的onTransact()方法来完成数据的发送。

    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
        {
          java.lang.String descriptor = DESCRIPTOR;
         case TRANSACTION_addPerson://整型值,代表第几个方法。
            {
              data.enforceInterface(descriptor);
              com.lxxl.binder_service.Person _arg0;
              if ((0!=data.readInt())) {
                _arg0 = com.lxxl.binder_service.Person.CREATOR.createFromParcel(data);
              }
              else {
                _arg0 = null;
              }
              this.addPerson(_arg0);
              reply.writeNoException();
              return true;
            }
    }
    

    至此,数据已经到用户空间到内核空间了,服务端怎么接收数据呢?

    public IBinder onBind(Intent intent) {
            persons = new ArrayList<>();
            Log.e("AidlService", "success onBind");
            return iBinder;
        }
    
        private IBinder iBinder = new IMyAidlInterface.Stub() {
            @Override
            public void addPerson(Person person) throws RemoteException {
              //来自客户端的数据
                persons.add(person);
            }
    
            @Override
            public List<Person> getPersonList() throws RemoteException {
                return persons;
            }
        };
    

    通过Stub.onTransact()来完成。

    总结

    建议还是不借助Aidl的情况下手写一遍加强对binder机制的理解。

    相关文章

      网友评论

        本文标题:binder机制 源码解读

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