由于Binder在Android的信息传输中占有比较重要的作用,所以把对Binder的分析单独出一篇文章来记录一下。
Binder,翻译为粘合剂,在Android进程间通讯相关的知识中经常出现。一般来说对Binder的解释通常有以下几种:
借用大神 Carson_Ho的一张图来表示的话是下面这样的:
结合上图,大家应该可以对Binder有了一个较为清晰的定义了。
Binder主要用在Service,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间的通讯,所以较为简单。Messenger的底层实现是AIDL,所以分析AIDL中的Binder就能够帮助我们理解Binder的工作原理了。
写完AIDL文件之后,系统会在Build时生成一个继承IInterface接口的java文件。这个文件名和对应的AIDL文件名相同。在这个文件中,有一个内部类Stub,这个类就是Binder。
所以可以认为AIDL是为了帮助系统生成对应的Binder文件。
接下来我们写一个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 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的整体结构:
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 getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
}
这个类继承了IInterface
接口,同时这个类也是一个接口。这个接口申明了两个方法,也就是IBookManager.aidl
中的方法。然后声明了一个内部类Stub。这个Stub就是一个Binder类。在Stub内部还有个代理类Proxy,在跨进程通讯中个,它会是客户端的代理方法。
除去构造方法以外,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
对象。
这里主要的方法就是addBook
和getBookList
方法,其实这两个方法还是有部分相似之处的,这里先分析getBookList
方法
public java.util.List 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 _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.aidl中定义的接口方法。这两方法没啥说的,主要用来被Stub和Stub.Proxy来继承或实现的。
这里附上完整的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 _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 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 _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 getBookList() throws android.os.RemoteException;
public void addBook(com.wscq.aidltest.aidl.Book book) throws android.os.RemoteException;
}
相关文章: