Android开发艺术探索笔记(6)- Binder

进程间通信(IPC)有很多种方法,而Binder是其中一种。也可以说,Binder是在Android系统挂载的虚拟设备,设备的驱动为/dev/binder,在Linux上没有。Binder是客户端和服务端进行通信的媒介,通过Binder,客户端可以向服务端获取服务或者数据。这里将的服务有两种,一种是普通的服务,另一种是AIDL(Android Interface Define Language,Android接口定义语言)的服务。书中主要讲解通过Binder获取AIDL的服务。

我按照书中写了一个栗子:

Book.java

public class Book implements Parcelable {

    private int bookId;
    private String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

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

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

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeInt(bookId);
        parcel.writeString(bookName);
    }

}

Book.aidl

package com.johan.bookaidl;
parcelable Book;

IBookManager.aidl

package com.johan.bookaidl;
import com.johan.bookaidl.Book;
interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

使用Eclipse或者Android Studio等编程工具,build一下,会出来一个IBookManager.java文件,是一个接口来的。提醒:如果是用Android Studio自动生成的话,IBookManager.java文件在app\build\generated\source\aidl\debug\ com\johan\bookaidl(包名) 可以找到。我们看一下这个代码:

IBookManager.java

package com.johan.bookaidl;

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.johan.bookaidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.johan.bookaidl.IBookManager";

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

        /**
         * Cast an IBinder object into an com.johan.bookaidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.johan.bookaidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.johan.bookaidl.IBookManager))) {
                return ((com.johan.bookaidl.IBookManager) iin);
            }
            return new com.johan.bookaidl.IBookManager.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_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.johan.bookaidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.johan.bookaidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.johan.bookaidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.johan.bookaidl.IBookManager {
            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 java.util.List<com.johan.bookaidl.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.johan.bookaidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.johan.bookaidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.johan.bookaidl.Book book) 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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.johan.bookaidl.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.johan.bookaidl.Book book) throws android.os.RemoteException;
}

详细分析一下这个自动生成的代码结构,你就可以理解Binder的原理了。

(1)IBookManager是一个接口,而且继承了IInterface接口,书中说在Binder传输的接口都要继承IInterface接口,这是规定的,没什么好说了。看看里面的结构。在最外层一个Sub类和2个抽象方法,这两个抽象方法正是我们在IBookManager.aidl定义的两个方法。我们着重看一下Sub这个类。

(2)Sub类是一个IBookManager内部抽象类,继承了Binder类,同时还有一个IBookManager接口。每个属性和方法Look A Look,不要怕麻烦,没有多少代码(鼓励一下自己^_^)。

DESCRIPTOR
这个属性应该是作为Binder的唯一标识符,一般为全类名(com.johan.bookaidl.IBookManager)。

TRANSACTION_xxx
每个我们在aidl定义方法,都是有一个对应的code,在下面onTransact方法里面用到。

asInterface(android.os.IBinder obj)
由服务器的Binder转换客户端所需要的IBookManager对象,这个转换是区分进程的。如果在服务端和客户端在同一个进程,直接返回Sub本身。如果不在同一进程,返回的则是Stub.Porxy对象。

asBinder()
返回当前Binder对象。

onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
这个方法运行在服务端的Binder线城池里面。当客户端发起请求时,服务端会通过code确定调用的是个方法,从data取出参数,然后执行要调用的方法,再将返回的结果写入reply(如果目标方法有返回值)。如果返回的是false,客户端请求是失败。在这里可以做一些权限控制的事情(原来还可以这样)。

Proxy
从名字可以看出是一个Stub的代理类。这个类不是一个IBookManager接口实现类。在asInterface方法也知道,如果转换过程服务端和客户端不在同一个进程,那么需要返回这个代理类。我们着重看一下我们定义两个方法在这里的实现。

Proxy.getBookList()
这个方法先是定了两个List,一个用于传入参数的_data,一个用于读取结果的_reply,因为我们getBookList是有参数的,所以还定义了一个用于方法返回的_result。_data先赋值,然后客户端调用mRemote的onTransact方法发起RPC请求,线程会挂起,onTransact方法结束后,从_reply读取返回的结果,最后return从_reply返回的结果。Proxy.addBook()和这个方法原理相似,不再做分析。

书中指出,需要注意2点:

(1)客户端发起RPC时,线程会挂起,如果调用的方法比较耗时,则不能在UI线程发起RPC请求。
(2)不管RPC怎么耗时,都应该采用同步的方法请求。

如果上面看不懂,看这个图应该就明白了:

Binder机制

书中还介绍我们可以不用通过aidl生成java文件,有兴趣可以看书。

最后作者还说了,怎么去监听一个Binder连接断裂。Binder提供了两个方法去设置和解除死亡代理,分别是linkToDeath和unlinkToDeath。怎么去设置死亡代理呢?

我们先定义一个DeathRecipient这么一个死亡代理对象,然后在binderDied方法编写Binder断裂之后重连或者其他处理。

public class Binder.DeathRecipient deathRecipient = new Binder.DeathRecipient {
    @Override
    public void binderDied() {
        if (mBookManager == null) return;
        mBookManager.unlinkToDeath(deathRecipient, 0);
        mBookManager = null;
        // TODO
    }
}

然后设置代码:

service = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(deathRecipient, 0);

Binder其实还有个检查是否还连接的方法:isBinderAlive.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值