2.3.3 Binder
直观来说,Binder
是Android
中的一个类,它实现了IBinder
接口。从IPC
角度来说,Binder
是Android
中的一种跨进程通信方式,Binder
还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder
,该通信方式在Linux
中没有;从Android Framework
角度来说,Binder
是ServiceManager
连接各种Manager
(ActivityManager
,WindowManager
,等等)和相应ManagerService
的桥梁;从Android
应用层来说,Binder
是客户端和服务端进行通信的媒介,当bindService
的时候,服务端会返回一个包含了服务端业务调用的Binder
对象,通过这个Binder
对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL
的服务。
Android
开发中,Binder
主要用在Service
中,包括AIDL
和Messenger
,其中普通Service
中的Binder
不涉及进程间通信,所以较为简单,无法触及Binder
的核心,而Messenger
的底层其实是AIDL
,所以这里选择用AIDL
来分析Binder
的工作机制。为了分析Binder
的工作机制,我们需要新建一个AIDL
示例,SDK
会自动为我们生产AIDL
所对应的Binder
类。
// 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;
}
public Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator(){
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
/**
* 参数是一个Parcel,用它来存储和传输数据
* @param dest
*/
public void readFromParcel(Parcel dest) {
// 注意,此处的读值顺序应当是和writeToParcel()方法中一致的
bookId = dest.readInt();
bookName = dest.readString();
}
// 方便打印数据
@Override
public String toString() {
return "bookId: " + bookId + ",bookName: " + bookName;
}
}
// Book.aidl
// 第一类AIDL文件
// 这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用
// 注意:Book.aidl和Book.java的包名应当是一样的
package com.study.wumeng.aidl;
// 用import语句在这里声明任何非默认类型
// 注意parcelable是小写
parcelable Book;
// IBookManager.aidl
// 第二类AIDL文件
// 作用是定义方法接口
package com.study.wumeng.aidl;
// 用import语句在这里声明任何非默认类型
import com.study.wumeng.aidl.Book;
interface IBookManager {
/**
* 演示一些基本类型,您可以将它们用作参数并在AIDL中返回值。
*/
// 所有的返回值前都不需要加任何东西,不管是什么数据类型
List getBookList();
// 传参时除了Java基本数据类型以及String,CharSequence之外的类型
// 都需要在前面加上定向tag,具体加什么量需而定
void addBook(in Book book);
}
上面三个文件中,Book.java
是一个表示图书信息的类,它实现了Parcelable
接口。Book.aidl
是Book
类在AIDL
中的声明。IBookManager.aidl
是我们定义的一个接口,里面有两个方法:getBookList()
和addBook()
,其中getBookList()
用于从远程服务端获取图书列表,而addBook()
用于往图书列表中添加一本书,当然这两个方法主要是示例用,不一定要有实际意义。我们可以看到,尽管Book
类已经和IBookManager
位于相同的包中,但是在IBookManager
中仍然要导入Book
类,这就是AIDL
的特殊之处。
下面我们先看一下系统为IBookManager.aidl
生成的Binder
类,在build --- generated
目录下的包中有一个IBookManager.java
的类,这就是我们要找的类。接下来我们需要根据这个系统生成的Binder
类来分析Binder
的工作原理。
/*
* 这个文件是自动生成的。 不要修改.
* 原始文件: E:\\Android\\Aidl\\app\\src\\main\\aidl\\com\\study\\wumeng\\aidl\\IBookManager.aidl
*/
package com.study.wumeng.aidl;
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
* 本地端IPC实现存根类
*/
public static abstract class Stub extends android.os.Binder implements com.study.wumeng.aidl.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.study.wumeng.aidl.IBookManager";
/**
* Construct the stub at attach it to the interface.
* 在连接到接口时构建存根
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.study.wumeng.aidl.IBookManager interface,
* 将一个IBinder对象转换为com.study.wumeng.aidl.IBookManager接口
* generating a proxy if needed.
* 根据需要生成代理
*/
public static com.study.wumeng.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.study.wumeng.aidl.IBookManager))) {
return ((com.study.wumeng.aidl.IBookManager) iin);
}
return new com.study.wumeng.aidl.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 _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.study.wumeng.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.study.wumeng.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.study.wumeng.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;
}
/**
* 演示一些基本类型,您可以将它们用作参数并在AIDL中返回值。
*/// 所有的返回值前都不需要加任何东西,不管是什么数据类型
@Override
public java.util.List getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.study.wumeng.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
// 传参时除了Java基本数据类型以及String,CharSequence之外的类型
// 都需要在前面加上定向tag,具体加什么量需而定
@Override
public void addBook(com.study.wumeng.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);
}
/**
* 演示一些基本类型,您可以将它们用作参数并在AIDL中返回值。
*/// 所有的返回值前都不需要加任何东西,不管是什么数据类型
public java.util.List getBookList() throws android.os.RemoteException;
// 传参时除了Java基本数据类型以及String,CharSequence之外的类型
// 都需要在前面加上定向tag,具体加什么量需而定
public void addBook(com.study.wumeng.aidl.Book book) throws android.os.RemoteException;
}
上述代码是系统生成的,在gen
目录下,可以看到根据IBookManager.aidl
系统为我们生成了IBookManager.java
这个类,它继承了IInterface
这个接口,同时它自己也还是个接口,所有可以在Binder
中传输的接口都需要继承IInterface
接口。这个类刚开始看起来逻辑混乱,但是实际上还是很清晰的,通过它我们可以清除地了解到Binder
的工作机制。这个类的结构其实很简单,首先它声明了两个方法getBookList()
和addBook()
,显然这就是我们在IBookManager.aidl
中所声明的方法,同时它还声明了两个整型的id
分别用于标识这两个方法,这两个id
用于标识在transact
过程中客户端所请求的到底是哪个方法。接着,它声明了一个内部类Stub
,这个Stub
就是一个Binder
类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact
过程,而当两者位于不同进程时,方法调用需要走transact
过程,这个逻辑由Stub
的内部代理类Proxy
来完成。这么来看,IBookManager
这个接口的确很简单,但是我们也应该认识到,这个接口的核心实现就是它的内部类Stub
和Stub
的内部代理类Proxy
。
下面详细介绍针对这两个类的每个方法的含义。
DESCRIPTOR
Binder
的唯一标识,一般用当前Binder
的类名表示。
asInterface (android.os.IBinder obj)
用于将服务端的Binder
对象转换成客户端所需的AIDL
接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub
对象本身,否则返回的是系统封装后的Stub.proxy
对象。
asBinder()
此方法用于返回当前Binder
对象
onTransact
这个方法运行在服务端中的Binder
线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。该方法的原型为 public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
。服务端通过code
可以确定客户端所请求的目标方法是什么,接着从data
中取出目标方法所需的参数(如果目标方法有参数的话),然后执行目标方法。当目标方法执行完毕后,就向reply
中写入返回值(如果目标方法有返回值的话),onTransact
方法的执行过程就是这样的。需要注意的是,如果此方法返回false
,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证,毕竟我们也不希望随便一个进程都能远程调用我们的服务。
Proxy#getBookList
这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是这样的:首先创建该方法所需要的输入型Parcel
对象_data,
输出型Parcel
对象_reply
和返回值对象List
;然后把该方法的参数信息写入_data
中(如果有参数的话);接着调用transact
方法来发起RPC(远程过程调用)
请求,同时当前线程挂起;然后服务端的onTransact
方法会被调用,直到RPC
过程返回后,当前线程继续执行,并从_reply
中取出RPC
过程的返回结果;最后返回_reply
中的数据。
Proxy#addBook
这个方法运行在客户端,它的执行过程和getBookList
是一样的,addBook
没有返回值,所以它不需要从_reply
中取出返回值。
通过上面的分析,读者应该已经了解了Binder
的工作机制,但是有两点还是需要额外说明一下,首先,当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法是很耗时的,那么不能在UI
线程中发起此远程请求,其次,由于服务端的Binder
方法运行在Binder
的线程池中,所以Binder
方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中了。为了更好地说明Binder
,下面给出一个Binder
的工作机制图。
从上述分析过程来看,我们完全可以不提供AIDL
文件即可实现Binder
,之所以提供AIDL
文件,是为了方便系统为我们生成代码。系统根据AIDL
文件生成Java
文件的格式是固定的,我们可以抛开AIDL
文件直接写一个Binder
出来,接下来我们就介绍如何手动写一个Binder
。还是上面的例子,但是这次我们不提供AIDL
文件。参考上面系统自动生成的IBookManager.java
这个类的代码,可以发现这个类是相当有规律的,根据它的特点,我们完全可以自己写一个和它一模一样的类出来,然后这个不借助AIDL
文件的Binder
就完成了。但是我们发现系统生成的类主要由两部分组成,首先它本身是一个Binder
的接口(继承了IInterface
),其次它的内部有个Stub
类,这个类就是个Binder
。
private final IBookManager.Stub mBinder = new IBookManager.Stub() {
@Override
public List getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
synchronized (mBookList) {
if (!mBookList.contains(book)) {
mBookList.add(book);
}
}
}
};
首先我们会实现一个创建了一个Stub对象并在内部实现IBookManager的接口方法,然后在Service的onBind中返回这个Stub对象。因此,从这一点来看,我们完全可以把Stub类提取出来直接作为一个独立的Binder类来实现,这样IBookManager中就只剩接口本身了,通过这种分离的方式可以让它的结构变得清晰点。
根据上面的思想,手动实现一个Binder可以通过如下步骤来完成:
public interface IBookManager extends IInterface {
static final String DESCRIPTOR = "com.study.wumeng.aidl.IBookManager";
static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0;
static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1;
public List getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
}
可以看到,在接口中声明了一个Binder描述符和另外两个id,这两个id分别表示的是getBookList和addBook方法,这段代码原本也是系统生成的,我们仿造系统生成的规则去手动书写这部分代码。如果我们有三个方法,应该怎么做呢?很显然,我们要再声明一个id,然后按照固定模式声明这个新方法即可,这个比较好理解,不再多说。
实现Stub类和Stub类中的Proxy代理类,这段代码我们可以自己写,但是写出来后会发现和系统自动生成的代码是一样的,因此这个Stub类我们只需要参考系统生成的代码即可,只是结构上需要做一下调整,调整后的代码如下所示:
public class BookManagerImpl extends Binder implements IBookManager {
public BookManagerImpl() {
this.attachInterface(this,DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return (IBookManager) iin;
}
return new BookManagerImpl.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
List result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
Book arg0;
if ((0 != data.readInt())) {
arg0 = Book.CREATOR.createFromParcel(data);
} else {
arg0 = null;
}
this.addBook(arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
@Override
public List getBookList() throws RemoteException {
// 待实现
return null;
}
@Override
public void addBook(Book book) throws RemoteException {
// 待实现
}
private static class Proxy implements IBookManager {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public List getBookList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
List result;
try {
data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getBookList,data,reply,0);
reply.readException();
result = reply.createTypedArrayList(Book.CREATOR);
} finally {
reply.recycle();
data.recycle();
}
return result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (book != null) {
data.writeInt(1);
book.writeToParcel(data,0);
} else {
data.writeInt(0);
}
mRemote.transact(TRANSACTION_addBook,data,reply,0);
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
}
}
通过将上述代码和系统生成的代码对比,可以发现简直是一模一样的。也许有人会问:既然和系统生成的一模一样,那我们为什么要手动去写呢?我们在实际开发中完全可以通过AIDL
文件让系统去自动生成,手动去写的意义在于可以让我们更加理解Binder
的工作原理,同时也提供了一种不通过AIDL
文件来实现Binder
的新方式。也就是说,AIDL
文件并不是实现Binder
的必需品。如果是我们手写的Binder
,那么在服务端只需要创建一个BookManagerImpl
的对象并在Service
的onBind
方法中返回即可。最后,是否手动实现Binder
没有本质区别,二者的工作原理完全一样,AIDL
文件的本质是系统为我们提供了一种快速实现Binder
的工具,仅此而已。
接下来,我们介绍Binder
的两个很重要的方法linkToDeath
和unlinkToDeath
。我们知道,Binder
运行在服务端进程,如果服务端进程由于某种原因异常终止,这个时候我们到服务端的Binder
连接断裂(称之为Binder
死亡),会导致我们的远程调用失败。更为关键的是,如果我们不知道Binder
连接已经断裂,那么客户端的功能就会受到影响。为了解决这个问题,Binde
r中提供了两个配对的方法linkToDeath
和unlinkToDeath
,通过linkToDeath
我们可以给Binder
设置一个死亡代理,当Binder
死亡时,我们就会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。那么到底如何给Binder
设置死亡代理呢?也很简单。
首先,声明一个DeathRecipient
对象,DeathRecipient
是一个接口,其内部只有一个方法binderDied
,我们需要实现这个方法,当Binder
死亡的时候,系统就会回调binderDied
方法,然后我们就可以移出之前绑定的binder
代理并重新绑定远程服务。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
@Override
public void binderDied() {
if (mBookManager == null)
return;
mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
mBookManager = null;
// TODO;这里重新绑定远程Service
}
};
其次,在客户端绑定远程服务成功后,给binder
设置死亡代理;
mService = ImessageBoxManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient,0);
其中linkToDeath
的第二个参数是个标记位,我们直接设为0
即可。经过上面两个步骤,就给我们的Binder
设置了死亡代理,当Binder
死亡的时候我们就可以收到通知了。另外,通过Binder
的方法`isBinderAlive也可以判断Binder是否死亡。