浅析 Android 中 Binder 的上层原理

纸上得来终觉浅,绝知此事要躬行

Binder 一直是我心里的一个坎儿,因为不管是 Android 中的哪个组件,都总是会或多或少的涉及 Binder。对于 Binder 是 Android 为了提升其自身的进程间通信效率而发明的一种进程间通信的方式,其底层就涉及到了操作系统方面的一些内容,比如用户态、内核态和内存映射等等一系列的底层知识。

也因为它的底层体系太过复杂,所以一直不能很好的去理解它,其实知道今天也是这样的,但是通过一段时间的学习,自己对于 Binder 的上层应用逻辑有了一些小小的想法,其实在这个之前也有读过《Android 开发艺术探索》和和其他的一些博文,但可能是因为我之前的基础还是有些薄弱,所以总是不能很好的掌握其清晰的体系脉络。

因此今天趁着清明的假期,正好捋顺一下 Binder 和 AIDL 这部分的一些上层逻辑,也可以更好的帮助基础比较薄弱的同学,通过对 SDK 自动生成的 AIDL 对应的 Java 文件的每一部分进行详细解析,我相信可以让这些同学有一些新的收获,同时能够更好的理解 Binder 和 AIDL 的部分上层逻辑脉络,之后的时间里紧接着我会继续更新 Binder 和 AIDL 的一些相关应用和上层原理的文章。

这篇文章虽然是准备了也比较久了,但因为个人的水平有限,所以还可能存在一些问题和不足之处,所以如果您发现文章存在错误请及时联系我,我一定立即改正,避免影响到其他人的学习。

同时如果你也是热爱编程,对技术探索充满了热情,也可以关注我,我会保证坚持每星期更新两到三篇高质量的原创文章,涉及算法、Java、JVM、计网 和 Android 的一些知识,希望我们可以一同进步,共勉! 

Binder

首先 Binder 是 IBinder 的一个实现类,也是 Android 中一种常见的进程间的通信方式。对于他我们应该都不会感到陌生,尤其是在四大组件 Activity、BoardcastReceiver、ContentProvider 和 Service 的底层中都采用了 binder 的机制来进行进程间的通信。对于 AIDL 这种进程间通信方式的具体实现其实也是依托于 SDK 在我们写好 aidl 文件后为我们自动生成的 Binder 来实现的。

因为 Binder 的底层原理涉及到操作系统底层的内核态和用户态等等一些相关知识,脉络非常复杂,所以此文仅分析关于 Binder 的上层实现原理。我们应该知道 Messenger 的底层是由 AIDL 来实现的,而 AIDL 是通过自动生成 Binder 来实现进程间通信的,所以我们就从 AIDL 的进程间通信的方式开始进行分析,因为 Android 中开发者接触最多的进程间通信可能就是 Service ,所以我们就从 Service 的 Binder 开始。

首先,我们这次关于 Binder 分析的例子主要涉及这么六个个文件,即 Student.java 、Student.aidl 、IStudentManager.aidl、IStudentManager.java、ClientActivity.java 和 BackgroundService.java 。其中除了 IStudentManager.java 是由 SDK 生成的,其余的文件都是由我们自己来进行编写。下面我们将采用线性递进的方式开始进行分析,从而让读者能有更加清晰的脉络。

1)Student.java 和 Student.aidl

// Student.java
package com.example.multiprocesspractice.aidl;

import android.os.Parcel;
import android.os.Parcelable;

public class Student implements Parcelable {

    private int studentId;
    private String studentName;

    protected Student(Parcel in) {
        studentId = in.readInt();
        studentName = in.readString();
    }

    public Student(int studentId, String studentName){
        this.studentId = studentId;
        this.studentName = studentName;
    }

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

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

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

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


// Student.aidl
package com.example.multiprocesspractice.aidl;

parcelable Student;

以上是这两个文件的代码,仅作实例使用,我们可以看到这么几个比较关键的点,首先对于 Student 类在 Student.java 中我们是实现了 Parcelable 接口,实现这个接口的主要作用就是为了时该类可以被序列化从而进行进程间的传输(比如 Bundle 也实现了这个接口),需要注意的时这中序列化的方式是 Android 独有的一种方式,适用于进行进程间的较短的数据序列化传输和内存序列化,同时它的序列化过程是需用开发者来手动编写代码来实现的。与之相对的 Java 中另一种序列化的方式就是 Serializable 接口实现,这种序列化的方式是自动进行的,我们仅需要实现这个接口就可以了,但是这种方式的效率较低且开销较大,Android 中仅适用于存储设备存储等。

其次我们需要注意的是,对于每一个需要进程间传递的对象,其类型都必须创建一个与之相对应的 aidl 文件,并且声明为 Parcelable 类型名 的这种格式,不然编译时就会报错。

对于 Student 类中的具体代码就不过多解析了,因为涉及的都是 Parcelable 的一些知识点,所以下面我们接着来看 IStudentManager.aidl 文件。

2)IStudentManager.aidl

// IStudentManager.aidl
package com.example.multiprocesspractice.aidl;

// Declare any non-default types here with import statements
import com.example.multiprocesspractice.aidl.Student;

interface IStudentManager {

    void addStudent(in Student student);
    List<Student> getStudentList();
}

对于这个文件其实他就是就一个接口文件,在这个文件中我们规定了客户端可以对服务端进行调用的方法,而其具体的实现则要延迟到服务端的代码中去进行实现。同时,需要注意的是在这里尽管我们刚刚定义的 Student 类和当前 aidl 文件是位于同一路径下的,但是我们还是应当取进行显示的引入,否则编译无法通过。

然后,在这个 aidl 文件中,我们提供了规范的方法,同时如果方法中需要提供参数时,如果参数不为基本类型,我们应当显示指明其方向是 in 、out 还是 inout 。接下来我们就要分析所有文件中最重要的 IStudentManager.java 文件了。

3)IStudentManager.java

首先需要强调的是,这个文件是由 SDK 为我们自动生成的,而其生成的依据正是我们刚刚声明的那个 IStudentManager.aidl 文件。如果你使用的是 AndroidStudio 的话,选择 Android 模式,在 generatedJava 目录下的 项目名.aidl 文件下就可以找到这个文件,因为这个文件的排版是存在一些问题的,所以我进行了重新的排版。同时因为这个文件乍一看去会非常的复杂,所以我会将其进行分解,对每一部分来进行单独解析。首先我们来看它的最外层接口。

3.1)IStudentManager

package com.example.multiprocesspractice.aidl;

public interface IStudentManager extends android.os.IInterface {

    public void addStudent(com.example.multiprocesspractice.aidl.Student student) throws android.os.RemoteException;
    public java.util.List<com.example.multiprocesspractice.aidl.Student> getStudentList() throws android.os.RemoteException;

    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.example.multiprocesspractice.aidl.IStudentManager {...}
  
}

为了使整个文件的脉络更加清晰,因此我对其中的部分内容进行了缩略,便于我们对各个部分的逐步分析。首先是最外层的这个 IStudentManager 这个接口,我们会发现他是一个公共接口,同时实现了 IInterface 接口,需要注意的是这点与上面那个能够在 Binder 中传输的类都需要实现 Parcelable 一样,所有能够在 Binder 传输的接口都应当实现 IInterface 接口。

然后我们会发现这个接口其实跟我们刚刚定义的那个 IStudentManager.aidl 中的的接口十分类似,也是定义了这两个方法,但是不同的是它多出来了一个抽象的静态内部类 Stub ,并且其继承了 Binder 类,并且实现了我们外部的那个 IStudentManager 接口中的方法,所以我们可以推断出这个 Stub 其实就是我们所使用的 Binder 类,因此我们继续进到 Stub 类中去进行进一步的探索。

3.2)Stub

    public static abstract class Stub extends android.os.Binder implements com.example.multiprocesspractice.aidl.IStudentManager {
        private static final java.lang.String DESCRIPTOR = "com.example.multiprocesspractice.aidl.IStudentManager";

        /** Construct the stub at attach it to the interface. */
        public Stub() {...}

        /**
         * Cast an IBinder object into an com.example.multiprocesspractice.aidl.IStudentManager interface,
         * generating a proxy if needed.
         */
        public static com.example.multiprocesspractice.aidl.IStudentManager asInterface(android.os.IBinder obj) {...}

        @Override public android.os.IBinder asBinder() {...}
        @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {...}

        private static class Proxy implements com.example.multiprocesspractice.aidl.IStudentManager {...}

        static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

对于 Stub 类我们依然使用上面的方法来进行分析,即先不展开内部类和内部方法,只对其总体结构来进行分析。首先,我们看到的是 DESCRIPTOR 常量,这个常量含义就是 Binder 的标识符,用于确定 Binder 的唯一性,而其值一般就是当前 Binder 文件的全限定名称(文件路径)。

接下来就是构造方法,这个没什么好说的,就是根据当前 Binder 的标识符将当前 Binder 与接口进行绑定,下面分别是构造器的代码和其中 attachInterface 方法的源码。

    /** Construct the stub at attach it to the interface. */
    public Stub() {
       this.attachInterface(this, DESCRIPTOR);
    }

    /**
     * Convenience method for associating a specific interface with the Binder.
     * After calling, queryLocalInterface() will be implemented for you
     * to return the given owner IInterface when the corresponding
     * descriptor is requested.
     */
    public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

再往下就是 asInterface 方法,这个方法的作用正如其函数名,就是将服务端的 Binder 对象转化为 客户端的 aidl 接口类型对象,便于客户端来进行方法调用。根据下面的代码我们可以发现,它有三个返回的出口,首先如果传入的 Binder 为空,则直接返回 null ,其次如果传入传入的 Binder 不为空且当前客户端和服务端为同一进程通信,那么就直接将 Stub 作为结果返回,否则就是服务端和客户端不再同一进程,那么就是跨进程通信,此时我们就需要返回一个本地代理对象。对于代理对象的介绍我们放在下面来说,接下来我们再看 asBinder 方法。

        /**
         * Cast an IBinder object into an com.example.multiprocesspractice.aidl.IStudentManager interface,
         * generating a proxy if needed.
         */
        public static com.example.multiprocesspractice.aidl.IStudentManager asInterface(android.os.IBinder obj) {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.example.multiprocesspractice.aidl.IStudentManager))) {
                return ((com.example.multiprocesspractice.aidl.IStudentManager)iin);
            }
            return new com.example.multiprocesspractice.aidl.IStudentManager.Stub.Proxy(obj);
        }

对于 asBinder 方法也很好理解,即正如其方法名称那样其返回的就是当前的 Binder 对象。

        @Override public android.os.IBinder asBinder()
        {
            return this;
        }

接下来是 onTransact 方法,这个方法是运行在服务端的 Binder 线程池中的,主要用来执行服务端的方法分发,如果服务端和客户都但为异线程的时候,那么当客户端中的 Binder 类中的方法调用 transact 的时候,最后就会传递到这个方法中来进行执行。

而对于其的具体执行过程其实是比较好理解的,通过代码我们可以发现,它主要具有四个参数,第一个是 code ,也就是方法的标识号(就是 Stub 类中最后的那三个常量值,为了便于理解我就直接将这三个值贴到这里了),第二个是 Parcel 类型的输入值 data,其次是 Parcel 类型的输出值 reply,最后是一个标志数。

在方法执行的过程中,首先它会根据方法的标识号来选取对应的执行代码,然后从 data 中通过 createFromParcel 来取出输入参数,之后调用当前 Binder 线程池中的本地方法(例如 this.addStudent)来进行执行,当执行完毕后再将执行的结果写入 reply 中返回即可。

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

        @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_addStudent: {
                    data.enforceInterface(descriptor);
                    com.example.multiprocesspractice.aidl.Student _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = com.example.multiprocesspractice.aidl.Student.CREATOR.createFromParcel(data);
                    }
                    else {
                        _arg0 = null;
                    }
                    this.addStudent(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getStudentList: {
                    data.enforceInterface(descriptor);
                    java.util.List<com.example.multiprocesspractice.aidl.Student> _result = this.getStudentList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    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;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

3.3)Proxy

分析方法依旧如上,我们只展开一些内容比较少的方法。这个 Proxy 静态内部类,其实就是一个实现了 IStudentManager 接口的本地代理类,因为我们通过上面的 asInterface 方法可以发现,假如当两端分别位于不同进程的时候,那么就会创建一个 Proxy 的实例对象返回,并且通过上面的 asInterface 方法我们可以看到,当实例化这个对象的时候,传入的参数就是服务端的 Binder,并在构造器中赋值给 mRemote 成员变量,这也就意味着,其实我们这个 Proxy 的实例对象是存在于客户端的,它使用服务端的 Binder(Proxy.mRemote)来完成进程间的通信。

剩下的方法就没什么好说的了,都是字面的意思,接下来我们来看其对于外部 IStudentManager 接口中两个方法的具体实现。

        private static class Proxy implements com.example.multiprocesspractice.aidl.IStudentManager {
            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 addStudent(com.example.multiprocesspractice.aidl.Student student) throws android.os.RemoteException {...}
            @Override public java.util.List<com.example.multiprocesspractice.aidl.Student> getStudentList() throws android.os.RemoteException {...}
        }

其实这两个方法具体实现是大致相同的,只不过是通过 transact 远程调用的方法不同。对于其执行的流程其实跟外部的两个方式是大致差不多的,都是先创建 Parcel 类型的 _data 的对象,然后将方法的参数写入,接着通过 transact 方法来进行远程过程请求,请求结束后当前线程会继续运行,然后从 _reply 中取出运行后的结果,最后将其作为执行结果返回。

但需要注意的是它是通过调用 mRemote.transact 来发送 RPC(远程过程调用)请求,会同时将客户端线程挂起。还记得我们上面说的那个 onTransact 方法么,它参数中的 code 正是从这里传入,用于判断所要执行的具体方法,而这个 mRemote 其实就是我们刚刚上面说到的初始化实例对象的时候传入的参数,也就是从服务端传过来的那个 Binder 对象。

所以其实我们可以发现,总结来说,在本地代理对象 Proxy 通过 transact 来进行 RFC 请求的时候,其实就只是将方法的标识、参数和结果来进行远程传递(因为其都为 Parcel 类型,可以跨进程传输),然后远程服务端的 onTransact 方法中通过传输过来的方法标识来进行判断具体需要执行的方法,然后使用传输过来的 Parcel 类型的 _data 对象中的方法参数来进行对应的方法调用,当方法调用结束后,再将最后的结果写入到 Parcel 类型的 _reply 对象中返回,这样就完成了一次完整的远程过程调用。

对于这个 mRemote 我还想多说一点,对于这个变量我追寻了很久,它的根源是服务端的 Service 中创建的那个 IStudentManager 对象,在这个对象中我们实现了具体的服务端的方法执行逻辑,然后在 onBinder 中将其作为返回值发送给了客户端,在客户端我们通过 bindService 绑定服务后,通过 ServiceConnection 对象中的 onServiceConnected 方法获取到它,再对其进行处理,在这个处理的过程中,我们通过 IStudentManager.Stub.asInterface 将其作为参数传入,然后在 asInterface 中通过进程异同性的判断,如果服务端和客户端为异进程,我们最后才将其发送到这个 Proxy 中。

            @Override public void addStudent(com.example.multiprocesspractice.aidl.Student student) 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 ((student!=null)) {
                        _data.writeInt(1);
                        student.writeToParcel(_data, 0);
                    }
                    else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
                    _reply.readException();
                    }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override public java.util.List<com.example.multiprocesspractice.aidl.Student> getStudentList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.multiprocesspractice.aidl.Student> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.multiprocesspractice.aidl.Student.CREATOR);
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

4)ClientActivity.java 和 BackgroundService.java

到这里其实整个 IStudentManager.java 文件我们其实就已经都分析完了,你们是不是觉得其实没有想象中的那么复杂,其实如果我们从大局出发来进行分析,然后再对每个点来进行逐个击破,其实这个 Binder 的上层应用结构就没有那么复杂。对于最后的两个客户端和服务端的文件,因为比较简单,这里就不做过多的讲解了,但为了与上面的那个 mRemote 解析相对应,所以还是把代码贴在这里。

public class ClientActivity extends AppCompatActivity {

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            IStudentManager studentManager = IStudentManager.Stub.asInterface(service);
            try {
                Log.i("CLIENT", " the student list " + studentManager.getStudentList().toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent service = new Intent(this, BackgroundService.class);
        bindService(service, mConnection, Context.BIND_AUTO_CREATE);
    }
}


public class BackgroundService extends Service {

    private CopyOnWriteArrayList<Student> mStudents = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IStudentManager.Stub() {
        @Override
        public void addStudent(Student student) throws RemoteException {
            mStudents.add(student);
        }

        @Override
        public List<Student> getStudentList() throws RemoteException {
            return mStudents;
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    };

    public BackgroundService() {
    }

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

    @Override
    public void onCreate() {
        super.onCreate();
        mStudents.add(new Student(1,"小白"));
        mStudents.add(new Student(2,"小黑"));
    }
}
发布了277 篇原创文章 · 获赞 33 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_40697071/article/details/89044089