Android跨进程通信分析

binder

Posted by River on March 1, 2022

前言

跨进程通信在日常开发中不常见,除了少部分多进程插件化架构的app;但在面试中比较常见;Android中,跨进程通信的方式 主要有文件,socket,管道,匿名共享内存以及大名鼎鼎的binder,本文重点讲binder。

1、传统的跨进程通信,如pipe

  1. 数据发送方进程将数据放在内存缓存区,通过系统调用陷入内核态;
  2. 内核程序在内核空间开辟一块内核缓存区,通过 copy_from_user函数将数据从数据发送方用户空间的内存缓存区拷贝到内核空间的内核缓存区中;
  3. 数据接收方进程在自己的用户空间开辟一块内存缓存区;
  4. 内核程序将内核缓存区中通过 copy_to_user 函数将数据拷贝到数据接收方进程的内存缓存区。

2、binder的跨进程通信

binder分析

Binder的英文含义为胶水,之所以叫binder驱动因为Linux动态内核的机制,binder模块独立编译只是运行时链接到内核 作为内核运行的一部分;比起其他跨进程通信在安全和效率上有明显优势;
安全方面binder的身份校验是Android权限的基础,性能上基于mmap少一次数据拷贝。

进程A和进程B通过Binder的IPC通信过程如下:

(1) Binder驱动在内核空间创建一块数据接收缓存区S1,内核在内核空间有一块内存缓存区S2(S1和S2都在内核空间创建);
(2) 进程B数据接收方的内存缓存区记为S3,建立S1和S2,S1和S3的映射关系;
(3) 进程A通过系统调用copy_from_user函数将数据从进程A的内存缓存区拷贝到内核缓存区S2;
(4) 由于S2和S1建立了地址映射关系,相当于拷贝数据到S1;
(5) 由于S1和S3也建立了地址映射关系,相当于拷贝数据到S3;
(6) 这样只做了一次数据拷贝就实现了跨进程通信的整个过程,如上图所示。

从上面的分析可以看出,mmap地址映射非常实用并且高效的;在之前的博文一种埋点存储方案也是用了mmap。

3、从aidl的角度看Binder使用

3.1、Binder是Binder通信的实体实现,实现了IBinder接口,内部几乎都是native方法; 编码中都是以IBinder接口的形式传递Binder实例,完成跨进程通信行为。

3.2、aidl是Android Interface Definition Language的简称,安卓接口定义语言; 通过自动生成的抽象类Stub简化Binder的使用,显而易见内部还是通过Binder完成的。

3.3、进程A(Client)和进程B(Server)编码分析

定义一个User的Java文件以及一个aidl文件,一个UserManager接口的aidl文件

// 定义User的java文件实现Parcelable接口
public class User implements Parcelable {
    public int id;
    public String name;

    public User() {}

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    protected User(Parcel in) {
        id = in.readInt();
        name = in.readString();
    }


    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}

// User的aidl文件
package com.me.liqianjiang.binder;

parcelable User;


// UserManager的aidl文件
package com.me.liqianjiang.binder;

import com.me.guanpj.binder.User;

interface UserManager {
    void addUser(in User user);

    List<User> getUserList();
}

进程B(Server)的实现

在Manifest注册一个独立进程Service,在Service的onBind方法返回一个Binder实例。


 <service android:name=".UserServer"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.me.liqianjiang.binder"/>
            </intent-filter>
        </service>

public class UserServer extends Service {

    static class ServiceBinderImpl extends UserManager.Stub {

        List<User> users = new ArrayList<>();

        private ICallback mICallback;

        @Override
        public void addUser(User user) {
            Log.e("gpj", "进程:" + Utils.getProcessName()
                    + ",线程:" + Thread.currentThread().getName() + "————Server 执行 addUser");
            users.add(user);
        }

        @Override
        public List<User> getUserList() {
            Log.e("gpj", "进程:" + Utils.getProcessName()
                    + ",线程:" + Thread.currentThread().getName() + "————Server 执行 getUserList");
            if (mICallback != null){
                try {
                    mICallback.callback(Thread.currentThread().getName());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            return users;
        }

        @Override
        public void registerListener(ICallback call) throws RemoteException {
            mICallback = call;
        }
    }

    private final ServiceBinderImpl mServerBinderObject = new ServiceBinderImpl();

    @Override
    public IBinder onBind(Intent intent) {
        Log.e("gpj", "进程:" + Utils.getProcessName()
                + ",线程:" + Thread.currentThread().getName() + "————Server onBind");
        return mServerBinderObject;
    }
}

进程A调用进程B(UserServer)

在进程A(主进程)的ClientActivity中,调用bindService创建进程B,也就是UserServer。

    private UserManager mRemoteServerReferenceProxy;
    
    private ServiceConnection mConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("gpj", "进程:" + Utils.getProcessName()
                    + ",线程:" + Thread.currentThread().getName() + "————Client onServiceConnected");
            mRemoteServerReferenceProxy = UserManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteServerReferenceProxy = null;
        }
    };
    
    
    private void bindService() {
        Intent intent = new Intent();
        intent.setAction("com.me.guanpj.binder");
        intent.setComponent(new ComponentName("com.me.liqianjiang.binder", "com.me.liqianjiang.binder.UserServer"));

        bindService(intent, mConn, Context.BIND_AUTO_CREATE);
    }
    
    // 某click方法向进程B添加数据
    mRemoteServerReferenceProxy.addUser(new User(111, "gpj"));
    
    // 某click方法向进程B读取数据
    List<User> userList = mRemoteServerReferenceProxy.getUserList();

aidl文件UserManager自动生成的抽象类Stub分析

  public static abstract class Stub extends android.os.Binder implements com.me.guanpj.binder.UserManager
  {
    private static final java.lang.String DESCRIPTOR = "com.me.guanpj.binder.UserManager";
    /** attach相当于往Binder的ServierManager注册一个Binder. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * 参数android.os.IBinder obj就是进程B(UserServer)的Binder实例的引用,通过对这个引用的持有包装成一个代理对象
     */
    public static com.me.guanpj.binder.UserManager asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.me.guanpj.binder.UserManager))) {
        return ((com.me.guanpj.binder.UserManager)iin);
      }
      return new com.me.guanpj.binder.UserManager.Stub.Proxy(obj);
    }
    
    // 进程B(UserServer)的Binder实例的引用
    @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
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      { // 第一个方法是建立连接 
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        // 读取进程A(Client进程)传过来的数据,传递给进程B
        case TRANSACTION_addUser:
        {
          data.enforceInterface(descriptor);
          com.me.guanpj.binder.User _arg0;
          if ((0!=data.readInt())) {
            _arg0 = com.me.guanpj.binder.User.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          this.addUser(_arg0);
          reply.writeNoException();
          return true;
        }
        // // 读取进程A(Client进程)传过来的数据,传递给进程B
        case TRANSACTION_getUserList:
        {
          data.enforceInterface(descriptor);
          java.util.List<com.me.guanpj.binder.User> _result = this.getUserList();
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    
    // Proxy通过持有进程B的Binder实例通过transact方法,也就是mRemote做进程间访问
    private static class Proxy implements com.me.guanpj.binder.UserManager
    {
      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 addUser(com.me.guanpj.binder.User user) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((user!=null)) {
            _data.writeInt(1);
            user.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().addUser(user);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      
      @Override public java.util.List<com.me.guanpj.binder.User> getUserList() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<com.me.guanpj.binder.User> _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getUserList, _data, _reply, 0);
          
          ...
          
          _reply.readException();
          _result = _reply.createTypedArrayList(com.me.guanpj.binder.User.CREATOR);
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }

      ... 
    }
    static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getUserList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    ... 
  }

总结

binder总结

  1. 通过对aidl生成代码的分析,其实进程间通信看似复杂;其实脉络清晰,通过IBinder持有Binder实例,调用transact方法 进行实际的跨进程访问;数据格式是android.os.Parcel;
  2. 进程间通信其实在设计上,也是笨笨滴通过DESCRIPTOR字符串和FIRST_CALL_TRANSACTION来识别信道和方法;
  3. 撇开aidl的自动生成代码,自定义Stub一样能完成基于Binder的跨进程通过;
  4. 课后作业,实现一个跨进程通信的事件监听;形式参考如下。
interface ICallback {
    void callback(String tag);
}


interface UserManager {
    void addUser(in User user);

    List<User> getUserList();

    void  registerListener(ICallback call);
}