Binder框架

作者: yjiyjige | 来源:发表于2016-06-06 00:47 被阅读457次

    Binder是一种架构,分为三个部分组成:

    • 服务端接口
    • Binder驱动
    • 客户端接口

    对于服务端,一个Binder服务端就是一个Binder类的对象,该对象一旦创建,内部就启动一个隐藏线程。该线程接下来会接收Binder驱动发送过来的消息,收到消息后,会执行到Binder对象中的onTransact方法,根据按照不同的参数执行不同的服务端方法。

    服务端重载onTransact方法主要是把该方法的参数转化为服务端方法的参数,而该方法的参数来源于客户端调用transact方法时的输入。所以,如果transact有固有格式的输入,那么onTransact就会有固定格式的输出。

    对于Binder驱动,任意一个服务端Binder对象被创建时,同时会在Binder驱动中创建一个mRemote对象,该对象的类型也是Binder。客户端调用远程服务时,调用的其实是mRemote对象。在Binder驱动中,mRemote被重载,主要做以下事情:

    • 以线程间消息通信的模式,向服务端发送客户端传过来的参数
    • 挂起当前线程(客户端线程),等待服务端线程执行完特定的服务端函数后通知
    • 接收服务端线程的通知,然后继续执行客户端线程,返回客户端代码区

    从上面的流程来看,对于客户端似乎是直接调用到了服务端的Binder,但实际上是通过Binder驱动进行了中转,即存在两个Binder对象。

    服务端设计

    对于服务端,需要做的事情是重写onTransact方法:

    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    }
    

    其中的code就是与客户端约定好的调用哪个方法的标识。比如约定当code的值为100时,调用服务端的play方法;datareplay分别是传递给方法的参数和方法的返回值,需要根据具体方法读取和写入。

    客户端设计

    对于客户端,第一步应该是从Binder驱动中获得与远程服务端对应的mRemote引用。比如在Activity中通过调用bindService,如果绑定服务成功,可以获得一个IBinder对象,这个就是Binder驱动中的mRemote。调用mRemote引用的transact方法,Binder驱动会挂起调用的当前客户端线程,然后向远程服务发送一个消息,该消息中包含了客户端传递的数据。服务端调用完具体的方法后将返回值放入reply(如果有返回值的话),并向Binder发送一个notify的消息,使得客户端线程重新被唤醒,完成调用。

    AIDL生成的代码分析

    从上文的分析可以看出,其实对于远程服务端的调用,都是基于约定的。比如对于codedatareply我们需要明确地赋予其意义。

    • code的值对应哪个服务端方法,相应的data中有哪些值、需要往reply中写入哪些值
    • datareplay的数据写入顺序是怎样的

    而AIDL工具就是帮我们处理这些繁琐事情的。以一个简单的AIDL分析:

    // 文件名:IRemoteService.aidl
    package com.demo;
    
    interface IRemoteService {
        void sayHello();
    
        String getValue();
    }
    

    看起来和Java的接口定义差不多,只不过文件的拓展名是.aidl不是.java

    通过AIDL工具会在com.demo包下生成一个IRemoteService.java文件,这个才是给程序使用的。生成的文件主要包括三个部分的内容:

    • 一个IRemoteService接口
    • 一个IRemoteService.Stub的抽象类
    • IRemoteService.Stub中定义一个Proxy的内部类

    下面具体分析:

    // IRemoteService接口部分
    package com.demo;
    
    public interface IRemoteService extends android.os.IInterface {
        public void sayHello() throws android.os.RemoteException;
    
        public java.lang.String getValue() throws android.os.RemoteException;
    }
    

    这部分没什么好说的,就是将AIDL中的方法转化为Java接口的方法。

    public static abstract class Stub extends android.os.Binder implements com.demo.IRemoteService {
        // 规定code的值
        static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        private static final java.lang.String DESCRIPTOR = "com.demo.IRemoteService";
    
        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
    
        /**
         * Cast an IBinder object into an com.demo.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.demo.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.demo.IRemoteService))) {
                return ((com.demo.IRemoteService) iin);
            }
            return new com.demo.IRemoteService.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_sayHello: {
                    data.enforceInterface(DESCRIPTOR);
                    this.sayHello();
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getValue: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getValue();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    
        private static class Proxy implements com.demo.IRemoteService {
            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 sayHello() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
    
            @Override
            public java.lang.String getValue() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
    }
    

    代码看起来很长,但职责还是很明确的,一开始就规定了几个对应的int值,通过该值去映射到具体的服务端方法。

    Stub类继承了Binder,重写了onTransact方法,帮我们完成了code值到对应方法的映射以及方法参数的读取与返回值的写入。Stubimplements了IRemoteService,但并没有实现,显然这部分是让子类去实现的。

    Proxy类实现了IRemoteService中的两个方法,方法中实现的数据写入和StubonTransact的数据读取是对应的,解决了数据顺序的问题。

    对于服务端,我们只需要继承Stub,然后实现IRemoteService接口中的方法;对于客户端,我们拿到IBinder后,可以通过StubasInterface方法,得到一个接口的实现类,其实也就是Proxy的实例,之后就可以通过这个实例直接调用IRemoteService的方法了。

    小结:通过AIDL,帮我们生成的代码,我们就不用考虑transactonTransact该如何实现,从而可以把精力放在具体的接口逻辑实现上。

    相关文章

      网友评论

        本文标题:Binder框架

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