前言
简单来讲,Binder 是 Android 中的一个类,它实现了 IBinder 接口。从 IPC 角度来讲,Binder 是 Android 中的一种跨进程通信方式,可以将 Binder 理解为一种虚拟的物理设备,它的设备驱动是 /dev/binder,该通信方式在 Linux 中并没有。
Binder作用初见
-
从 Android Framework 角度来说:
Binder 是 ServiceManager 连接各种 Manager (ActivityManager、WindowManager等)和相应 ManagerService 的桥梁。
-
从 Android 应用层来说:
Binder 是客户端和服务端进行通信的媒介, 当 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于 AIDL 的服务。
Binder 工作机制
Android 开发中,Binder 主要用在 Service 中,包括 AIDL 和 Messenger,其中普通 Service 中的 Binder 不涉及进程间通信,较为简单,无法触及 Binder 的核心。而 Messenger 的底层其实是 AIDL,所以这里选择 AIDL 来分析 Binder 的工作机制。
1、新建 java 和 aidl 文件
文件结构如下:
Book.java
package com.glp.binder.aidl;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by Guolipeng on 2017/9/8.
* 通过 AIDL 来分析 Binder 的工作过程
*/
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return ;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.bookId);
dest.writeString(this.bookName);
}
protected Book(Parcel in) {
this.bookId = in.readInt();
this.bookName = in.readString();
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}
Book.aidl
package com.glp.binder.aidl;
parcelable Book;
IBookManager.aidl
package com.glp.binder.aidl;
import com.glp.binder.aidl.Book;
// 【注意】尽管 Book 类已经和 IBookManager 位于相同的包中,但是在 IBookManager 中仍然需要导入 Book 类,这就是 AIDL 的特殊之处。
interface IBookManager {
List<Book> getBookList(); // 用于从远程服务端获取图书列表
void addBook(in Book book); // 用于往图书列表中添加一本书
}
上面三个文件中:
- Book.java 表示图书信息的类,实现了 Parcelable 接口
- Book.aidl 是 Book 类在 AIDL 中的声明
- IBookManager.aidl 是我们定义的一个接口,定义我们将来需要的方法
2、构建自动生成
然后我们构建一下项目,就会在
app\build\generated\source\aidl\debug\
目录下找到系统为我们自动生成的
com.glp.binder.aidl.IBookManager.java
文件。下面通过在自动生成的 java 类中进行代码注释的方式,分析一下 Binder 的工作原理:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\Wiwide\\WindowStudy\\app\\src\\main\\aidl\\com\\glp\\binder\\aidl\\IBookManager.aidl
*/
package com.glp.binder.aidl;
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
// 声明了内部类 Stub 就是一个 Binder 类
// 当客户端和服务端都位于同一个进程时,方法调用不会走 transact 过程 (transact [træn'zækt] 交易;谈判;处理)
// 当两者位于不同进程时,方法调用需要走跨进程的 transact 过程,这个逻辑由 Stub 的内部代理类 Proxy 来完成 (proxy ['prɒksɪ] n. 代理人;委托书)
public static abstract class Stub extends android.os.Binder implements com.glp.binder.aidl.IBookManager {
// Binder 的唯一标识,一般用当前 Binder 的类名表示
private static final java.lang.String DESCRIPTOR = "com.glp.binder.aidl.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.glp.binder.aidl.IBookManager interface,
* generating a proxy if needed.
*/
// 用于将服务端的 Binder 对象转换成客户端所需要的 AIDL 接口类型的对象,该转换过程是区分进程的
// 客户端和服务端位于同一进程,此方法返回的就是服务端的 Stub 本身,否则返回的是系统封装后的 Stub.Proxy 对象
public static com.glp.binder.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.glp.binder.aidl.IBookManager))) {
return ((com.glp.binder.aidl.IBookManager) iin);
}
return new com.glp.binder.aidl.IBookManager.Stub.Proxy(obj);
}
// 返回当前的 Binder 对象
@Override
public android.os.IBinder asBinder() {
return this;
}
// 该方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
// 服务端通过code来确定客户端请求的目标方法是什么
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
// 目标方法无参数
java.util.List<com.glp.binder.aidl.Book> _result = this.getBookList();
reply.writeNoException();
// 目标方法执行完成之后,向reply中写入返回值(如果目标方法有返回值的话)
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
// 从data中取出目标方法所需要的参数(如果目标方法有参数的话),然后去执行目标方法
com.glp.binder.aidl.Book _arg0;
if (( != data.readInt())) {
_arg0 = com.glp.binder.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
// 目标方法无返回值
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
// 如果 onTransact 方法返回false,那么客户端请求就会失败,可以利用这个特性来做权限验证
//(也不希望随便一个进程都能远程调用我们的服务)
}
// Stub 的内部代理类 Proxy
private static class Proxy implements com.glp.binder.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;
}
// 在 IBookManager.aidl 中声明的方法(运行在客户端)
@Override
public java.util.List<com.glp.binder.aidl.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain(); // 创建方法所需要的输入型Parcel对象 _data
android.os.Parcel _reply = android.os.Parcel.obtain(); // 创建方法所需要的输出型Parcel对象 _reply
java.util.List<com.glp.binder.aidl.Book> _result; // 创建方法返回值对象 _result
try {
// 方法无参数
_data.writeInterfaceToken(DESCRIPTOR);
// 调用 transact 方法来发起 RPC(远程过程调用)请求,同时当前线程挂起
// 然后服务端的 onTransact 方法会被调用,直到 RPC 过程返回后,当前线程继续执行
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, );
_reply.readException();
// 从 _reply 中取出 RPC 过程的返回结果
_result = _reply.createTypedArrayList(com.glp.binder.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
// 最后返回 结果
return _result;
}
// 在 IBookManager.aidl 中声明的方法(运行在客户端)
@Override
public void addBook(com.glp.binder.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);
// 将方法参数写进 _data(如果有参数的话)
if ((book != null)) {
_data.writeInt();
book.writeToParcel(_data, );
} else {
_data.writeInt();
}
// 调用 transact 方法来发起 RPC(远程过程调用)请求,同时当前线程挂起
// 然后服务端的 onTransact 方法会被调用,直到 RPC 过程返回后,当前线程继续执行
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, );
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
// 声明两个整型的id分别用于标识两个方法,这两个标识用于区分在跨进程的 transact 过程中客户端请求的到底是哪个方法
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + );
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + );
}
public java.util.List<com.glp.binder.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.glp.binder.aidl.Book book) throws android.os.RemoteException;
}
通过上述代码我们可以发现,系统根据 IBookManager.aidl 为我们自动生成的 IBookManager.java 类继承了 IInterface 接口,同时它自己还是个接口。【注意】所有可以在 Binder 中传输的接口都需要继承 IInterface 接口。
3、额外说明
- 当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法是很耗时的,那么不能在 UI 线程中发起远程请求。
- 由于服务端的 Binder 方法运行在 Binder 的线程池中,所以 Binder 方法不管是否耗时,都应该采用同步的方式去实现,因为它已经运行在一个线程中了。
4、Binder 工作机制图
补充
我们知道,Binder 运行在服务端进程中,如果服务端进程由于某种原因异常终止,这个时候我们到服务端的 Binder 连接断裂(称为 Binder 死亡),会导致我们的远程调用失败。更为关键的是,如果我们不知道 Binder 连接已经断裂,那么客户端的功能就会受到影响。为了解决这个问题,Binder 中提供了两个配对的方法 linkToDeath 和 unlinkToDeath。通过 linkToDeath 可以给 Binder 设置一个死亡代理,当 Binder 死亡时,我们会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。
设置死亡代理示例:
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
if (mRemoteBookManager == null)
return;
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, );
mRemoteBookManager = null;
// TODO:这里重新绑定远程Service
}
};
首先,声明一个 DeathRecipient 对象。DeathRecipient 是一个接口,其内部只有一个方法 binderDied,我们需要实现这个方法,当 Binder 死亡的时候,系统会回调 binderDied 方法,然后我们就可以移除之前绑定的 binder 代理并重新绑定远程服务。
其次,在客户端绑定远程服务成功后,给 binder 设置死亡代理:
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
mRemoteBookManager = bookManager;
try {
mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, );
... ...
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
mRemoteBookManager = null;
Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
}
};
其中 linkToDeath 的第二个参数是个标记位,我们直接设为 0 即可。经过上面两个步骤,就给我们的 Binder 设置了死亡代理,当 Binder 死亡的时候我们就可以收到通知了。另外,通过 Binder 的方法 isBinderAlive 也可以判断 Binder 是否死亡。