Android中的Binder详解
Binder简介
由于Binder在Android的信息传输中占有比较重要的作用,所以把对Binder的分析单独出一篇文章来记录一下。
-
什么是Binder
Binder,翻译为粘合剂,在Android进程间通讯相关的知识中经常出现。一般来说对Binder的解释通常有以下几种:
- Binder是Android中的一个类,实现了IBinder接口。
- Binder是Android独有的一种跨进程通信方式
- Binder是一种虚拟的物理设备,可以用来连通客户端与服务端
借用大神 Carson_Ho的一张图来表示的话是下面这样的:
结合上图,大家应该可以对Binder有了一个较为清晰的定义了。
-
Binder的使用场景
Binder主要用在Service,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间的通讯,所以较为简单。Messenger的底层实现是AIDL,所以分析AIDL中的Binder就能够帮助我们理解Binder的工作原理了。
Binder结构分析
-
AIDL和Binder的关系
写完AIDL文件之后,系统会在Build时生成一个继承IInterface接口的java文件。这个文件名和对应的AIDL文件名相同。在这个文件中,有一个内部类Stub,这个类就是Binder。
所以可以认为AIDL是为了帮助系统生成对应的Binder文件。
-
生成Binder的AIDL文件
接下来我们写一个AIDL文件,AIDL的文件内容在下面的代码区域,如果想了解AIDL的整个流程,可以参考这篇文章
// 文件名是:Book.aidl package com.wscq.aidltest.aidl; //aidl中用到了实现了序列化的类Book,所以这里需要申明一下 parcelable Book;
// 文件名是:IBookManager.aidl package com.wscq.aidltest.aidl; import com.wscq.aidltest.aidl.Book; import com.wscq.aidltest.aidl.IOnNewBookArrivedListener; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); }
然后在java目录下需要有一个实现了Parcelable接口的Book.java类
public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } //省略get、set、Parcelable和toString方法 }
上述文件写完以后,在AndroidStudio中会立即生成对应的Binder文件,位置在工程目录下
/build/generated/source/aidl/debug/
中。具体的路径如图所示:如果没有可以
build
一下,报错的话需要再检查一遍aidl是否书写正确或者放置的路径是否正确。在AndroidStudio中,正确的目录结构如下: -
Binder的总体结构
在获取到对应的Binder文件后,我们先来看一下Binder的整体结构:
public interface IBookManager extends IInterface { //声明内部类Stub,这个就是一个Binder类。 public static abstract class Stub extends Binder implements IBookManager { //Binder的唯一标识,一般用当前Binder的类名表示 private static final String DESCRIPTOR = "com.wscq.aidltest.aidl.IBookManager"; //... //客户端的代理类Proxy private static class Proxy implements IBookManager { //... } //这两个整型的ID用于标识在跨进程调用中。客户端到底调用的是哪个方法 static final int TRANSACTION_getBookList = (FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (FIRST_CALL_TRANSACTION + 1); } //声明两个方法,也就是IBookManager.aidl中的方法 public List<Book> getBookList() throws RemoteException; public void addBook(Book book) throws RemoteException; }
这个类继承了
IInterface
接口,同时这个类也是一个接口。这个接口申明了两个方法,也就是IBookManager.aidl
中的方法。然后声明了一个内部类Stub。这个Stub就是一个Binder类。在Stub内部还有个代理类Proxy,在跨进程通讯中个,它会是客户端的代理方法。 -
Binder内方法详细分析
-
首先看看Stub中的各个方法
除去构造方法以外,Stub中的方法还有
asBinder()
、asInterface()
和onTransact
方法。在这三个方法之外,还有上文提到过的,两个静态ID,用来标识客户端调用的方法。其中
asBinder()
方法相当于一个get方法,用来返回当前的Binder对象,这个代码比较简单,我们略过。接下来我们看一下
onTransact()
方法,这个方法运行在服务端,会通过code来分发具体要执行的方法。方法参数中的各个值代表的意义可以看下面注释:/** * 运行在服务端中的Binder线程池中,当客户端发起跨进程请求时, * 远程请求会通过系统底层封装后交由此方法处理 * * @param code 通过此code可以确定客户端所请求的目标方法是什么 * @param data 目标方法中所需要的参数 * @param reply 目标方法执行完后,向reply中写入返回值(若有) * @param flags 启动方式,这里并没有使用 * @return false 表示请求失败, true表示请求成功 * @throws android.os.RemoteException */ @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { //根据code来分发,运行对应的方法 switch (code) { //... return true; } } return super.onTransact(code, data, reply, flags); }
然后我们看下
asInterface()
方法:public static com.wscq.aidltest.aidl.IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.wscq.aidltest.aidl.IBookManager))) { return ((com.wscq.aidltest.aidl.IBookManager) iin); } return new com.wscq.aidltest.aidl.IBookManager.Stub.Proxy(obj); }
这个方法会判断当前服务端和客户端是否处于同一进程中。如果处于同一个进程中,会返回同一个Stub对象本身,如果处于不同的进程会返回封装后的客户端代理类
Stub.proxy
。这个方法会在客户端调用,用来获取一个IBookManager
对象。 -
然后看看客户端代理Stub.Proxy中的方法
这里主要的方法就是
addBook
和getBookList
方法,其实这两个方法还是有部分相似之处的,这里先分析getBookList
方法public java.util.List<com.wscq.aidltest.aidl.Book> getBookList() throws android.os.RemoteException { //客户端的参数合计,也就是Stub中onTransact的data参数 android.os.Parcel _data = android.os.Parcel.obtain(); //输出型对象,也就是Stub中onTransaction的reply参数 android.os.Parcel _reply = android.os.Parcel.obtain(); //返回值对象 java.util.List<com.wscq.aidltest.aidl.Book> _result; try { //写入参数到_data中 _data.writeInterfaceToken(DESCRIPTOR); //发起远程请求,当前线程会暂时挂起 mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); //获取服务端返回信息 _reply.readException(); //从_reply中读取信息,构造_result _result = _reply.createTypedArrayList(com.wscq.aidltest.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } //最后返回_result return _result; }
概括的来说,当客户端调动此方法时,会创建服务端的参数
_data
和接受返回的_reply
以及返回值对象_result
,然后把参数信息写入_data
中。这里开始调用transact
方法发起远程请求,同时当前线程挂起,当Stub的onTransact
执行完后,再次回到当前方法,从_reply
中取值,构造_result
,然后返回_result
。addBook
方法的过程和上述几乎一致,大家可以在最后面的完整代码中对照查看。 -
最后看IBookManager中的方法
除去上面的两个内部类以外,剩余的代码就只有两个抽象的接口了,也就是IBookManager.aidl中定义的接口方法。这两方法没啥说的,主要用来被Stub和Stub.Proxy来继承或实现的。
-
-
上述Binder的完整代码
这里附上完整的Binder对象,方便大家进行一些整体的研究:
public interface IBookManager extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.wscq.aidltest.aidl.IBookManager { //Binder的唯一标识,一般用当前列名表示 private static final java.lang.String DESCRIPTOR = "com.wscq.aidltest.aidl.IBookManager"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这个转换过程是区分进程的, * 如果客户单服务端处于同一进程,此方法返回服务端的Stub,否则返回的是系统封装后的Stub.proxy对象 * * @param obj 服务端的Binder对象 * @return 客户端所需的AIDL接口对象 */ public static com.wscq.aidltest.aidl.IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.wscq.aidltest.aidl.IBookManager))) { return ((com.wscq.aidltest.aidl.IBookManager) iin); } return new com.wscq.aidltest.aidl.IBookManager.Stub.Proxy(obj); } /** * 用于返回当前的Binder对象 * * @return 当前binder对象 */ @Override public android.os.IBinder asBinder() { return this; } /** * 运行在服务端中的Binder线程池中,当客户端发起跨进程请求时, * 远程请求会通过系统底层封装后交由此方法处理 * * @param code 通过此code可以确定客户端所请求的目标方法是什么 * @param data 包含目标方法所需要的参数 * @param reply 目标方法执行完后,回想reply中写入返回值(若有) * @param flags * @return false表示请求失败, true表示请求成功 * @throws android.os.RemoteException */ @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.wscq.aidltest.aidl.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.wscq.aidltest.aidl.Book _arg0; if ((0 != data.readInt())) { _arg0 = com.wscq.aidltest.aidl.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.wscq.aidltest.aidl.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; } /** * @return 返回_reply中的数据 * @throws android.os.RemoteException */ @Override public java.util.List<com.wscq.aidltest.aidl.Book> getBookList() throws android.os.RemoteException { //客户端的参数合计,也就是Stub中onTransact的data参数 android.os.Parcel _data = android.os.Parcel.obtain(); //输出型对象,也就是Stub中onTransaction的reply参数 android.os.Parcel _reply = android.os.Parcel.obtain(); //返回值对象 java.util.List<com.wscq.aidltest.aidl.Book> _result; try { //写入参数到_data中 _data.writeInterfaceToken(DESCRIPTOR); //发起远程请求,当前线程会暂时挂起 mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); //获取服务端返回信息 _reply.readException(); //从_reply中读取信息 _result = _reply.createTypedArrayList(com.wscq.aidltest.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } /** * 和上面的方法类似,不过由于没有返回值,所以不需要从_reply中取出返回值 * * @param book 要添加的书籍信息 * @throws android.os.RemoteException */ @Override public void addBook(com.wscq.aidltest.aidl.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.wscq.aidltest.aidl.Book> getBookList() throws android.os.RemoteException; public void addBook(com.wscq.aidltest.aidl.Book book) throws android.os.RemoteException; }
相关文章:
上一篇: C++自学笔记
推荐阅读
-
Android中的Binder详解
-
Spring-Test:详解Spring中的Profile 博客分类: Spring-Test Spring-TestProfile
-
Android中ListView上拉刷新的功能实现(总结)
-
Android中Context的继承关系详解
-
C++中inline的用法详解
-
Android中竖着的Tablayout的简单使用
-
Django中的cookie与session详解和理解
-
dubbo中的一致性hash(ConsistentHashLoadBalance)详解
-
一文详解self-attention机制在语义分割中的应用(含论文解析)
-
JS 中的apply方法、call方法详解 博客分类: javascript