Android开发Android技术知识首页投稿(暂停使用,暂停投稿)

Binder框架

2016-06-06  本文已影响457人  yjiyjige

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我们需要明确地赋予其意义。

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

// 文件名:IRemoteService.aidl
package com.demo;

interface IRemoteService {
    void sayHello();

    String getValue();
}

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

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

下面具体分析:

// 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该如何实现,从而可以把精力放在具体的接口逻辑实现上。

上一篇下一篇

猜你喜欢

热点阅读