乐趣区

关于android:不得不说的Android-Binder机制与AIDL

说起 Android 的过程间通信,想必大家都会不谋而合的想起 Android 中的 Binder 机制。而提起 Binder,想必也有不少同学会想起初学 Android 时被 Binder 和 AIDL 摆布的恐惧感。然而作为一个 Android 开发者,Binder 是咱们必须把握的常识。因为它是构架整个 Android 大厦的钢筋和混凝土,连贯了 Android 各个系统服务和下层利用。只有理解了 Binder 机制能力更加深刻的了解 Android 开发和 Android Framework。这也是为什么无论是《Android 开发艺术摸索》还是《深刻了解 Android 内核波及思维》这些进阶类书籍把过程间通信和 Binder 机制放到靠前章节的起因,它太重要了,重要到整个 Android Framework 都离不开 Binder 的身影。

本篇文章咱们暂且不去探讨 Binder 的底层实现,因为就目前而言,笔者把握的水平也不足以去输入 Binder 实现原理的的内容。因而,为了不误导大家,这里就来写一写 Binder 的根底用法以及 AIDL。尽管对于 Binder 和 AIDL 的根底用法网上的内容亘古未有。然而能把 Binder 和 AIDL 写的浅显易懂的文章并不多见。也就导致了很多人感觉 Binder 跟 AIDL 的应用都很难。

本篇文章,我将通过我本人的学习思路来带大家意识 Binder 机制和 AIDL。

一、过程间通信

在操作系统中,每个过程都有一块独立的内存空间。为了保障程序的的安全性,操作系统都会有一套严格的平安机制来禁止过程间的非法拜访。毕竟,如果你的 APP 能拜访到别的 APP 的运行空间,或者别的 APP 能够轻而易举的拜访到你 APP 的运行空间,设想一下你是不是解体的心都有了。所以,操作系统层面会对利用过程进行内存隔离,以保障 APP 的运行平安。然而,很多状况下过程间也是须要互相通信的,例如剪贴板的性能,能够从一个程序中复制信息到另一个程序。这就是过程间通信诞生的背景。

狭义的讲,过程间通信(Inter-process communication, 简称 IPC)是指运行在不同过程中的若干线程间的数据交换。

操作系统中常见的过程间通信形式有共享内存、管道、UDS 以及 Binder 等。对于这些过程间的通信形式本篇文章咱们不做深究,理解即可。

  • 共享内存(Shared Memory) 共享内存形式实现过程间通信依附的是申请一块内存区域,让后将这块内存映射到过程空间中,这样两个过程都能够间接拜访这块内存。在进行过程间通信时,两个过程能够利用这块内存空间进行数据交换。通过这样的形式,缩小了数据的赋值操作,因而共享内存实现过程间通信在速度上有显著劣势。
  • 管道(Pipe) 管道也是操作系统中常见的一种过程间通信形式,Windows 零碎过程间的通信依赖于此中形式实现。在进行过程间通信时,会在两个过程间建设一根领有读(read)写 (write) 性能的管道, 一个过程写数据,另一个过程能够读取数据,从而实现过程间通信问题。
  • UDS(UNIX Domain Socket) UDS 也被称为 IPC Socket,但它有别于 network 的 Socket。UDS 的外部实现不依赖于 TCP/IP 协定,而是基于本机的“安全可靠操作”实现。UDS 这种过程间通信形式在 Android 中用到的也是比拟多的。
  • Binder Binder 是 Android 中独有的一种过程间通信形式。它底层依附 mmap, 只须要一次数据拷贝,把一块物理内存同时映射到内核和指标过程的用户空间。

本篇文章,咱们重点就是理解如何应用 Binder 实现过程间通信。Binder 仅仅从名字来看就给人一种很神秘的感觉,就因为这个名字可能就会吓走不少初学者。但其实 Binder 自身并没有很神秘,它仅仅是 Android 零碎提供给开发者的一种过程间通信形式。

而从上述几种过程间通信形式来看,无论是哪种过程间通信,都是须要一个过程提供数据,一个过程获取数据。因而,咱们能够把提供数据的一端称为 服务端 ,把获取数据的一端称为 客户端。从这个角度来看 Binder 是不是就有点相似于 HTTP 协定了?所以,你齐全能够把 Binder 当成是一种 HTTP 协定,客户端通过 Binder 来获取服务端的数据。意识到这一点,再看 Binder 的应用就会简略多了。

二、应用 Binder 实现过程间通信

应用 Binder 实现过程间通信其实非常简单。咱们举一个查问问题的例子,服务端提供依据学生姓名查问学生问题的接口,客户端连贯服务端通过学生姓名来查问问题,而客户端与服务端的媒介就是 Binder。

1. 服务端的实现

服务端天然是要提供服务的,因而就须要咱们开启一个 Service 期待客户端的连贯。对于 Android 的 Service 这里就不必多说了吧,咱们实现一个 GradeService 并继承 Service,来提供问题查问接口。代码如下:

public class GradeService extends Service {
    public static final int REQUEST_CODE=1000;
    private final Binder mBinder = new Binder() {
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {if (code == REQUEST_CODE) {String name = data.readString();
                // 依据姓名查问学生问题并将问题写入到返回数据
                int studentGrade = getStudentGrade(name);
                if (reply != null)
                    reply.writeInt(studentGrade);
                return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
        // 依据姓名查问学生问题
        public int getStudentGrade(String name) {return StudentMap.getStudentGrade(name);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {return mBinder;}
}

因为咱们要实现的是跨过程通信,因而,咱们将这个服务端的 Service 设置到近程过程中,在 AndroidManifest 文件中如下:

<service
    android:name="com.zhpan.sample.binder.server.GradeService"
    android:process=":server">
    <intent-filter>
        <action android:name="android.intent.action.server.gradeservice" />
    </intent-filter>
</service>

就这样,一个近程的,提供问题查问的服务就实现了。

2. 客户端的实现

客户端自然而然的是要连贯服务端过程问题查问。因而,咱们在客户端的 Activity 中取绑定 GradeService 进行问题查问。代码如下:

public class BinderActivity extends AppCompatActivity {
    // 近程服务的 Binder 代理
    private IBinder mRemoteBinder;

    private final ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            // 获取近程服务的 Binder 代理
            mRemoteBinder = iBinder;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {mRemoteBinder = null;}
    };



    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_binder);
        // 绑定服务
        findViewById(R.id.btn_bind_service).setOnClickListener(view -> bindGradeService());
        // 查问学生问题
        findViewById(R.id.btn_find_grade).setOnClickListener(view -> getStudentGrade("Anna"));
    }
    // 绑定近程服务
    private void bindGradeService() {
        String action = "android.intent.action.server.gradeservice";
        Intent intent = new Intent(action);
        intent.setPackage(getPackageName());
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }
    // 从近程服务查问学生问题
    private int getStudentGrade(String name) {Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        int grade = 0;
        data.writeString(name);
        try {if (mRemoteBinder == null) {throw new IllegalStateException("Need Bind Remote Server...");
            }
            mRemoteBinder.transact(REQUEST_CODE, data, reply, 0);
            grade = reply.readInt();} catch (RemoteException e) {e.printStackTrace();
        }
        return grade;
    }

客户端的代码就是通过绑定近程服务,而后获取到服务的 Binder 代理,来查问学生问题。可见,应用 Binder 实现过程间通信是非常简单的,能够说简略的有点出乎所料。那咱们之前写的 AIDL 是什么呢?AIDL 生成的那一堆是什么玩意儿?咱们且往下看。

三、代理模式优化 Binder 应用

尽管上一章中的代码曾经非常简单了,然而还是有能够优化的空间。咱们能够通过设计模式来进行优化,让代码更加简洁。

首先须要定义一个接口查问问题的接口 IGradeInterface,代码如下:

public interface IGradeInterface {
    // 查问问题接口
    int getStudentGrade(String name);
}

1. 服务端代码优化

接着,对服务端的代码进行优化。咱们实现一个自定义的 GradeBinder,并实现上述接口,代码如下:

public class GradeBinder extends Binder implements IGradeInterface {

    @Override
    public int getStudentGrade(String name) {return StudentMap.getStudentGrade(name);
    }

    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {if (code == REQUEST_CODE) {String name = data.readString();
            int studentGrade = getStudentGrade(name);
            if (reply != null)
                reply.writeInt(studentGrade);
            return true;
        }
        return super.onTransact(code, data, reply, flags);
    }
}

上述代码将查问问题的相干逻辑从 Service 搬到了 GradeBinder 中。因而,此时 Service 中只须要在 onBind 的时候返回 GradeBinder 的实例即可。代码如下:

public class GradeService extends Service {

    public static final int REQUEST_CODE=1000;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {return new GradeBinder();
    }
}

2. 客户端代码优化

客户端优化的思路是在连贯到近程服务时候实例化一个代理类,代理类持有 Binder,让代理类行使 Binder 的权力。首先来看代理类的代码实现:

public class BinderProxy implements IGradeInterface {
    // 被代理的 Binder
    private final IBinder mBinder;
    // 私有化构造方法
    private BinderProxy(IBinder binder) {mBinder = binder;}

    // 通过 Binde 读取问题
    @Override
    public int getStudentGrade(String name) {Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        int grade = 0;
        data.writeString(name);
        try {if (mBinder == null) {throw new IllegalStateException("Need Bind Remote Server...");
            }
            mBinder.transact(REQUEST_CODE, data, reply, 0);
            grade = reply.readInt();} catch (RemoteException e) {e.printStackTrace();
        }
        return grade;
    }
        
    // 实例化 Binder 代理类的对象
    public static IGradeInterface asInterface(IBinder iBinder) {if (iBinder == null) {return null;} 
        
        if (iBinder instanceof IGradeInterface) {LogUtils.e("以后过程");
            // 如果是同一个过程的申请,则间接返回 Binder
            return (IGradeInterface) iBinder;
        } else {LogUtils.e("近程过程");
            // 如果是跨过程查问则返回 Binder 的代理对象
            return new BinderProxy(iBinder);
        }
    }

}

BinderProxy 类的构造方法被设置成了 private。同时提供了一个 asInterface 办法中,这个办法通过判断 Binder 是不是 IGradeInterface 类型从而确定是不是跨过程的通信。如果不是跨过程通信,则返回以后这个 Binder,否则就返回 Binder 的这个代理类。

接下来客户端连贯上近程服务的时候应用 BinderProxy 获取 Binder 或者 BinderProxy 实例。代码如下:

public class BinderProxyActivity extends BaseViewBindingActivity<ActivityBinderBinding> {
    // 此处可能是 BinderProxy 也可能是 GradeBinder
    private IGradeInterface mBinderProxy;

    private final ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            // 连贯服务胜利,依据是否跨过程获取 BinderProxy 或者 GradeBinder 实例
            mBinderProxy = BinderProxy.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {mBinderProxy = null;}
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        binding.btnBindService.setOnClickListener(view -> bindGradeService());
          // 查问学生问题点击事件,通过 mBinderProxy 查问问题
        binding.btnFindGrade.setOnClickListener(view -> ToastUtils.showShort("Anna grade is" + mBinderProxy.getStudentGrade("Anna")));
    }

    // 绑定服务
    private void bindGradeService() {
        String action = "android.intent.action.server.gradeservice";
        Intent intent = new Intent(action);
        intent.setPackage(getPackageName());
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

}

能够看到,此时的代码相比第一章的代码整洁了不少。然而,代码写起来仿佛还没有第一章中的不便。次要是因为要咱们减少一个 IGradeInterface 接口,还要自定义一个 GradeBinder,同时,还须要写代理类的相干代码,感觉十分繁琐。那么有没有方法让代码简洁,写起来还不繁琐呢?答案是必定的,应用 AIDL 就能够实现。

四、AIDL

AIDL 是 Android Interface Description Languaged 简写。用于刻画客户端 / 服务端通信接口的一种描述语言。提起 AIDL 置信很多人都会感觉头大,定义一个 AIDL 接口,生成了那么多不知所云的代码,看起来几乎就是劫难。先别放心,如果你看懂了第三章的内容,那么其实你曾经齐全把握了 AIDL。没错,说白了 AIDL 生成的那一坨代码其实就是咱们第三章中写的代码。即 AIDL 的原理其实就是应用了代理模式对 Binder 的应用进行了优化,应用 AIDL 保障了代码的整洁,同时也省去了本人编写繁琐的代理类相干代码。

对于 AIDL 的应用就非常简单了。

1. 创立 AIDL 接口

首先,在要创立 AIDL 的目录上右键 ->New->AIDL->AIDl File 来创立一个 AIDL 文件,如下图所示:

创立一个名为 IGradeService 的 AIDL 文件,并增加一个 getStudentGrade 的办法。代码如下:

// IGradeService.aidl
package com.zhpan.sample.binder.aidl;

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

interface IGradeService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    int getStudentGrade(String name);
}

接着 Rebuild 一下我的项目后 IDE 就会主动生成 AIDL 的代码了。

2.AIDL 生成的代码

在我的项目的 build 目录下 com.zhpan.sample.binder.aidl 包中会看到主动生成的一个名为 IGradeService 的接口,代码如下:

// 这个接口相当于上一章中的 IGradeInterface 接口
public interface IGradeService extends android.os.IInterface {
  
  ...
  
  // Stub 是一个 Binder,相当于上一章中的 GradeBinder
  public static abstract class Stub extends android.os.Binder
      implements com.zhpan.sample.binder.aidl.IGradeService {
    private static final java.lang.String DESCRIPTOR = "com.zhpan.sample.binder.aidl.IGradeService";

    public Stub() {this.attachInterface(this, DESCRIPTOR);
    }

    public static IGradeService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin != null) && (iin instanceof com.zhpan.sample.binder.aidl.IGradeService))) {
        // 如果是以后过程则间接返回以后 Binder 对象
        return ((com.zhpan.sample.binder.aidl.IGradeService) iin);
      }
      // 跨过程则返回 Binder 的代理对象
      return new com.zhpan.sample.binder.aidl.IGradeService.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_basicTypes: {data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          long _arg1;
          _arg1 = data.readLong();
          boolean _arg2;
          _arg2 = (0 != data.readInt());
          float _arg3;
          _arg3 = data.readFloat();
          double _arg4;
          _arg4 = data.readDouble();
          java.lang.String _arg5;
          _arg5 = data.readString();
          this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_getStudentGrade: {data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          int _result = this.getStudentGrade(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default: {return super.onTransact(code, data, reply, flags);
        }
      }
    }
    // Binder 的代理类,相当于上一章中的 BinderProxy
    private static class Proxy implements com.zhpan.sample.binder.aidl.IGradeService {
      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 basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
          double aDouble, java.lang.String aString) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {_data.writeInterfaceToken(DESCRIPTOR);
          _data.writeInt(anInt);
          _data.writeLong(aLong);
          _data.writeInt(((aBoolean) ? (1) : (0)));
          _data.writeFloat(aFloat);
          _data.writeDouble(aDouble);
          _data.writeString(aString);
          boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
            return;
          }
          _reply.readException();} finally {_reply.recycle();
          _data.recycle();}
      }

      @Override public int getStudentGrade(java.lang.String name)
          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);
          _data.writeString(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentGrade, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {return getDefaultImpl().getStudentGrade(name);
          }
          _reply.readException();
          _result = _reply.readInt();} finally {_reply.recycle();
          _data.recycle();}
        return _result;
      }

      public static com.zhpan.sample.binder.aidl.IGradeService sDefaultImpl;
    }

    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getStudentGrade = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

    public static boolean setDefaultImpl(com.zhpan.sample.binder.aidl.IGradeService impl) {if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }

    public static com.zhpan.sample.binder.aidl.IGradeService getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}
  }

  /**
   * Demonstrates some basic types that you can use as parameters
   * and return values in AIDL.
   */
  public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble,
      java.lang.String aString) throws android.os.RemoteException;

  public int getStudentGrade(java.lang.String name) throws android.os.RemoteException;
}

瞥一眼代码会发现 IGradeService 接口中有一个名为 Stub 的外部类,它继承了 Binder,并实现了 IGradeService 接口,并且它的外部有一个 asInterface 的办法,这个办法与咱们上一章 BinderProxy 中的 asInterface 统一,只是写的地位不同而已。另外在 Stub 的 onTranscation 办法的 TRANSACTION\_getStudentGrade 条件中的代码与 GradeBinder 的 onTranscation 办法代码是一样的。

接着,Stub 类中还有一个名为 Proxy 的外部类。Proxy 类与上一章的 BinderProxy 绝对应。能够看到 Proxy 类的构造方法并没有修饰符,而 BinderProxy 的构造方法被申明成了 private,都能够避免内部通过构造方法区实例化代理类的对象。Proxy 的 getStudentGrade 办法与 BinderProxy 中的 getStudentGrade 一样,通过 Binder 去读取服务端的写入数据。

3.AIDL 客户端

应用 AIDL 的客户端实现简直与第三章中的代码统一。只不过是在连贯到服务端后通过 IGradeService.Stub 下的 asInterface 办法来获取 Binder 或者 Binder 的代理对象。代码如下:

public class AidlActivity extends BaseViewBindingActivity<ActivityBinderBinding> {

    private IGradeService mBinderProxy;

    private final ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            // 连贯服务后,依据是否跨过程获取 Binder 或者 Binder 的代理对象
            mBinderProxy = IGradeService.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {mBinderProxy = null;}
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        binding.btnBindService.setOnClickListener(view -> bindGradeService());
          // 查问学生问题
        binding.btnFindGrade.setOnClickListener(view -> getStudentGrade("Anna"));
    }
  
    // 绑定服务
    private void bindGradeService() {
        String action = "android.intent.action.server.aidl.gradeservice";
        Intent intent = new Intent(action);
        intent.setPackage(getPackageName());
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }
  
    // 查问问题
    private void getStudentGrade(String name) {
        int grade = 0;
        try {grade = mBinderProxy.getStudentGrade(name);
        } catch (RemoteException e) {e.printStackTrace();
        }
        ToastUtils.showShort("Anna grade is" + grade);
    }
}

到这里,对于 AIDL 的介绍就完结了。有没有惊奇的发现 AIDL 原来这么简略!

五、总结

本篇文章次要带大家意识了过程间通信和 Binder 与 AIDL 的应用。通过本篇文章的学习能够发现 Binder 与 AIDL 其实是非常简单的。理解了 Binder 之后,咱们就能够去更加深刻的学习 Android Framework 层的常识了。

视频
无所不能的 Binder 底层原理解析
过程间通信原理

原文:https://juejin.cn/post/6994057245113729038

退出移动版