FrameWork-进程间通信之Binder机制AIDL

进程间通信之Binder机制

  • Binder是什么?
  • 为什么要使用Binder?
  • Binder原理
  • Binder使用实战

1、Binder是什么

Binder 是一种进程间通信机制,基于开源的 OpenBinder 实现;OpenBinder 起初由 Be Inc. 开发,后由 Plam Inc. 接手。从字面上来解释 Binder 有胶水、粘合剂的意思,顾名思义就是粘和不同的进程,使之实现通信


2、为什么要使用Binder

各种进程间通信方式对比

传统Linux进程间通信方式:管道、信号量、socket、共享内存,而Binder是android系统独有的。

  • Binder:只需要拷贝一次,基于c/s架构,易用性高,系统为每个APP分配UID同时支持实名和匿名更安全
  • 共享内存:无需拷贝,控制复杂,易用性差,依赖上层协议,访问接入点是开放的不安全
  • Socket:需要拷贝两次,基于c/s架构,作为一款通用接口,其传输效率低,开销大,以来上层协议,访问接入点是开放的,不安全

Android系统需要一种高效率,安全性高的方式,也就是Binder,binder只需要拷贝一次,仅次于共享内存,而且采用传统的c/s结构


3.Binder实现机制

3.1 Linux系统关于内存的几个概念

首先需要明确Linux系统关于内存的几个概念:虚拟内存用户空间内核空间MMap

  • 虚拟内存

虚拟内存简单来说,是一种内存管理技术,是虚拟的、逻辑上存在的存储空间;将物理上(不连续的物理)的内存(碎片)形成一个逻辑上连续完整的地址空间。虚拟内存初始化的过程,就叫做内存映射。实际原理请自行百度吧,当时学计算机原理的时候头秃了很久。

我们的用户程序操作的内存实际上都是通过虚拟内存操作真正的物理内存。详细可见我的另一篇帖子什么是虚拟内存?虚拟内存的原理

  • 用户空间、内核空间

虚拟内存被操作系统划分成两块:用户空间内核空间,用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。为了安全,他们是隔离的,即使用户的程序崩溃了,内核也不受影响。

32位系统,即2^32,即总共可以访问地址为4G.内核空间为1G,用户空间为3G;64位操作系统,低位2-47位是有效的可变地址,高位48-63位全补0对应用户空间,全补1是内核空间

  • MMap(Memory Mapping)内存映射

Linux通过将虚拟内存区域与磁盘上的空间关联起来,以初始化这个虚拟内存区域的内容,这个过程成为内存映射。具体一点来说,把一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间的映射关系。实现这种映射关系之后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写到对应的磁盘文件。

示意图如下:

FrameWork-进程间通信之Binder机制AIDL_第1张图片

用户空间(虚拟内存)和磁盘文件(物理内存)存在映射关系,这样在虚拟内存中操作文件,就会自动回写到磁盘文件中。

3.2 Binder驱动启动流程

Binder驱动启动流程主要有3个步骤:

  • binder_init()驱动启动

主要做3件事:

  • 为binder设备分配内存;
  • 然后初始化binder设备;
  • 最后把binder设备添加到设备链表里
  • binder_open()驱动打开
  • 创建binder_proc对象;
  • 当前进程信息保存到proc,这样proc就保存了当前进程信息
  • 把proc把proc添加到binder_proc链表中
  • binder_mmap()
  • 通过用户空间的虚拟内存大小分配一块内核的虚拟内存,这两个大小是一样的;
  • 分配一块物理内存–1页4kb;现在还没有通信,先分配这么多,用到的时候再分配;这块儿物理内存同样和虚拟内存的大小一样
  • 把这块物理内存分别映射到用户空间虚拟内存和内核空间虚拟内存;

如图所示:

FrameWork-进程间通信之Binder机制AIDL_第2张图片

:intent传递数据大小是1MB-8kb,异步方式是一半。因为intent传递数据依靠binder,这个大小是binder驱动初始化的时候分配的

整个安卓Binder进程通信的内存映射机制就如下图所示:

FrameWork-进程间通信之Binder机制AIDL_第3张图片


4.Binder使用实战

4.1 在server端创建Service

class UserManagerService : Service() {
    private val mBinder =UserManagerBinder()
    override fun onCreate() {
        super.onCreate()
        Log.i(Companion.TAG, "onCreate: ")
    }

    override fun onBind(intent: Intent): IBinder {
        return mBinder
    }

    companion object {
        private const val TAG = "UserManagerService"
    }
}

然后把这个service注册到manifest

   <service
            android:name=".service.UserManagerService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.wilson.binder_server.action" />
            intent-filter>
        service>

注意exported要为true,否则不能由其他进程启动

4.2 在服务端java同级目录创建AIDL文件夹并编辑AIDL接口以及相关数据结构

FrameWork-进程间通信之Binder机制AIDL_第4张图片
注意图中框住的bean一定要同目录

package com.wilson.binder_server.beans;

// Declare any non-default types here with import statements

parcelable UserInfo;

package com.wilson.binder_server;

// Declare any non-default types here with import statements
import com.wilson.binder_server.beans.UserInfo;

interface IUserManager {

    void addUser(inout UserInfo user);
    boolean removeUser(inout UserInfo user);
    UserInfo getUser(in String name);
}

注意形参前面有inoutin

  • 定向 tag in 表示数据只能由客户端流向服务端,服务端将会收到客户端对象的完整数据,但是客户端对象不会因为服务端对传参的修改而发生变动。
  • out 表示数据只能由服务端流向客户端。服务端将会收到客户端对象,该对象不为空,但是它里面的字段为空,但是在服务端对该对象作任何修改之后客户端的传参对象都会同步改动。
  • inout 则表示数据可以在服务端与客户端之间双向流通。其中的数据流向是针对在客户端中的那个传入方法的对象而言的。服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

Javabean

public class UserInfo implements Parcelable {
    public String name;
    public int age;
    public int gender;

    public UserInfo(String name, int age, int gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public UserInfo() {
    }

    protected UserInfo(Parcel in) {
        name = in.readString();
        age = in.readInt();
        gender = in.readInt();
    }

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

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

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

    public void readFromParcel(Parcel in) {
        name = in.readString();
        age = in.readInt();
        gender = in.readInt();
    }

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

    @Override
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }
}

这个bean就是AIDL中的bean所指向的类;
bean需要实现Parcelable接口,由于我一开始的bean使用的Kotlin 的data class,没有手动实现接口,而是使用@Parcelize注解,结果发现编译之后,找不到Parcelable的方法,懒得去解决只好老老实实实现接口了

4.3 把服务端aidl整个文件以及java/kotlin的bean同目录拷贝到client

FrameWork-进程间通信之Binder机制AIDL_第5张图片

4.5 编译并实现自定义Binder

编译后在client和service端同时生成一个类文件:

package com.wilson.binder_server;
public interface IUserManager extends android.os.IInterface
{
  /** Default implementation for IUserManager. */
  public static class Default implements com.wilson.binder_server.IUserManager
  {
    @Override public void addUser(com.wilson.binder_server.beans.UserInfo user) throws android.os.RemoteException
    {
    }
    @Override public boolean removeUser(com.wilson.binder_server.beans.UserInfo user) throws android.os.RemoteException
    {
      return false;
    }
    @Override public com.wilson.binder_server.beans.UserInfo getUser(java.lang.String name) throws android.os.RemoteException
    {
      return null;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }

  /** Local-side IPC implementation stub class. 本地Binder需要实现/继承的接口*/
  public static abstract class Stub extends android.os.Binder implements com.wilson.binder_server.IUserManager
  {
    private static final java.lang.String DESCRIPTOR = "com.wilson.binder_server.IUserManager";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.wilson.binder_server.IUserManager interface,
     * generating a proxy if needed.
     */
    public static com.wilson.binder_server.IUserManager asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.wilson.binder_server.IUserManager))) {
        return ((com.wilson.binder_server.IUserManager)iin);
      }
      return new com.wilson.binder_server.IUserManager.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
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_addUser:
        {
          data.enforceInterface(descriptor);
          com.wilson.binder_server.beans.UserInfo _arg0;
          if ((0!=data.readInt())) {
            _arg0 = com.wilson.binder_server.beans.UserInfo.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          this.addUser(_arg0);
          reply.writeNoException();
          if ((_arg0!=null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        case TRANSACTION_removeUser:
        {
          data.enforceInterface(descriptor);
          com.wilson.binder_server.beans.UserInfo _arg0;
          if ((0!=data.readInt())) {
            _arg0 = com.wilson.binder_server.beans.UserInfo.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          boolean _result = this.removeUser(_arg0);
          reply.writeNoException();
          reply.writeInt(((_result)?(1):(0)));
          if ((_arg0!=null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        case TRANSACTION_getUser:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          com.wilson.binder_server.beans.UserInfo _result = this.getUser(_arg0);
          reply.writeNoException();
          if ((_result!=null)) {
            reply.writeInt(1);
            _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }


	//-----Binder代理类
    private static class Proxy implements com.wilson.binder_server.IUserManager
    {
      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.wilson.binder_server.beans.UserInfo 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();
          if ((0!=_reply.readInt())) {
            user.readFromParcel(_reply);
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public boolean removeUser(com.wilson.binder_server.beans.UserInfo user) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        boolean _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((user!=null)) {
            _data.writeInt(1);
            user.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_removeUser, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().removeUser(user);
          }
          _reply.readException();
          _result = (0!=_reply.readInt());
          if ((0!=_reply.readInt())) {
            user.readFromParcel(_reply);
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public com.wilson.binder_server.beans.UserInfo getUser(java.lang.String name) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        com.wilson.binder_server.beans.UserInfo _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getUser(name);
          }
          _reply.readException();
          if ((0!=_reply.readInt())) {
            _result = com.wilson.binder_server.beans.UserInfo.CREATOR.createFromParcel(_reply);
          }
          else {
            _result = null;
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.wilson.binder_server.IUserManager sDefaultImpl;
    }
    static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_removeUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    public static boolean setDefaultImpl(com.wilson.binder_server.IUserManager impl) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Stub.Proxy.sDefaultImpl != null) {
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.wilson.binder_server.IUserManager getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void addUser(com.wilson.binder_server.beans.UserInfo user) throws android.os.RemoteException;
  public boolean removeUser(com.wilson.binder_server.beans.UserInfo user) throws android.os.RemoteException;
  public com.wilson.binder_server.beans.UserInfo getUser(java.lang.String name) throws android.os.RemoteException;
}

这个类的原理下面说。

  • 然后在service端实现我们自己的Binder
class UserManagerBinder : IUserManager.Stub() {
    private val mUserManager by lazy { UserManager.getInstance() }

    init {
        Log.i(TAG, ": UserManagerBinder init ")
    }

    companion object {
        private const val GET_USER = Binder.FIRST_CALL_TRANSACTION + 1
        const val ADD_USER = GET_USER + 1
        private const val TAG = "UserManagerBinder"
    }

    override fun addUser(user: UserInfo?) {
        user?.let {
            mUserManager.addUser(user)
        }
    }

    override fun removeUser(user: UserInfo?): Boolean {
        user?.let { return mUserManager.removeUser(user) }
        return false
    }

    override fun getUser(name: String?): UserInfo {
        return mUserManager.getUser(name) ?: UserInfo()
    }

}

这个类需要继承我们IUserMnager的内部类Stub

然后在服务端的自定义Service里使用该Binder:

	private val mBinder =UserManagerBinder()
	override fun onBind(intent: Intent): IBinder {
        return mBinder
    }

4.6 编写Client端

class MainActivity : AppCompatActivity() {

    private var mUserManagerBinder: IUserManager? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //绑定服务
        findViewById<TextView>(R.id.tv_main).setOnClickListener {
            val serviceIntent = Intent().apply {
                setPackage("com.wilson.binder_server")
                action = "com.wilson.binder_server.action"
            }
            val success = bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE)
            Log.i(TAG, "bindService success=$success")
        }
		//向服务端发送数据
        findViewById<View>(R.id.btn_add_user).setOnClickListener {
            mUserManagerBinder?.addUser(UserInfo("wilson", 18, 1))
        }
        //从服务端获取数据
        findViewById<View>(R.id.btn_get_user).setOnClickListener {

            mUserManagerBinder?.getUser("wilson")?.let {
                Log.i(TAG, "get_user: user = [$it] ")
            }
        }

    }

	//链接回调
    private val mServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            service?.let {
                mUserManagerBinder = IUserManager.Stub.asInterface(service)
            }
            Log.i(Companion.TAG, "onServiceConnected: ")
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            mUserManagerBinder = null
            Log.i(Companion.TAG, "onServiceDisconnected: ")
        }

    }

    companion object {
        private const val TAG = "MainActivity"
    }
}

绑定服务有显式和隐式两种,参考我的另一篇帖子服务的启动方式。

需要注意,bindService返回结果并非绑定成功与否,它表示服务端是否存在,真正的绑定成功是ServiceConnection回调 onServiceConnected

代码一切完成,在client调用绑定方法,addUser和getUser方法:
FrameWork-进程间通信之Binder机制AIDL_第6张图片
所有代码已上传Github

4.7 AIDL编译生成的类解析

IUserManager 主要分为3部分

  • IUserManager接口定义
    这个就是我们实际需要调用的方法;
  • IUserManager.Stub内部类
    这个类继承了Binder同时实现了我们的IUserManager接口。这个类是client和service真正数据传递的的实现。

我们在client中,servieConnected方法中调用Stub.asInterface开始看:

  public static com.wilson.binder_server.IUserManager asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.wilson.binder_server.IUserManager))) {
        return ((com.wilson.binder_server.IUserManager)iin);
      }
      return new com.wilson.binder_server.IUserManager.Stub.Proxy(obj);
    }

这里的代码很简单,初次最终到最后一行,返回一个Proxy实例,并注入服务端Binder

Proxy是Stub的内部类,他们实现了相同的IUserManager接口,可以看到内部声明了一个变量mRemote就是通过构造器注入的服务端的Binder,它就是服务端Binder的代理。它的主要作用是向服务端传递数据和接收服务端传递过来的数据。

以addUser为例:

@Override public void addUser(com.wilson.binder_server.beans.UserInfo 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);
          }
          //调用服务端Binder
          boolean _status = mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().addUser(user);
            return;
          }
          _reply.readException();
          if ((0!=_reply.readInt())) {
            user.readFromParcel(_reply);
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }

这里把我们的业务数据序列化之后,调用mRemote.transact传递我们的数据达到代理的目的。为什么要代理呢,因为数据序列化反序列化模板类代码实现麻烦,聪明的设计师帮我们实现。

以上是针对于client端的原理,下面是service端。

上述说的服务端的Binder就是在server端我们自定义service中的onBind中我们返回了自定义的Binder实例。而我们的自定义Binder继承了Stub,我们聚焦与Stub。

Stub同样实现了IUserManager,在onTransact实现根据不同的方法标识,执行对应的数据处理,反序列化接口或者序列化返回数据。


这篇文章讲的有点乱,条理不够清晰,各位大佬将就着看吧,如果有问题,麻烦指正哈。
我要出门做核酸去了,哈哈哈哈哈

所有代码已上传Github

你可能感兴趣的:(Android架构师之路,Framwork,binder,linux,android)