Binder工作机制
我们可以先从SDK自动为我们生成的AIDL对应的Binder类来分析Binder工作机制。
建立AIDL
AIDL中使用了Binder,所以我们先通过AIDL来分析一下Binder工作过程。先创建一个Book类,实现Parcelable接口。
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}
创建两个AIDL文件,Book.aidl用来声明Book类,IBookManager.aidl为定义的接口,写两个方法。
Book.aidl
package com.utte.aidltest;
parcelable Book;
IBookManager.aidl
package com.utte.aidltest;
import com.utte.aidltest.Book;
interface IBookManager {
List getBookList();
void addBook(in Book book);
}
目录像这样
可以发现虽然Book.java在同一个包中,但是在IBookManager.aidl中用Book时还是需要import。
生成Binder类
此时如果build就会在generated目录下找到一个IBookManager.java,这是系统自动为IBookManager.aidl生成的Binder类。
虽然生成了IBookManager.java,但是可能会报错找不到Book类。
在Module:app的Gradle的android{}中加如下代码,就能解决了。
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
分析IBookManager.aidl
先看一下整体结构:
package com.utte.aidltest;
public interface IBookManager extends android.os.IInterface {
private static final java.lang.String DESCRIPTOR = "com.utte.aidltest.IBookManager";
public static abstract class Stub extends android.os.Binder implements com.utte.aidltest.IBookManager {
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.utte.aidltest.IBookManager asInterface(android.os.IBinder 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 {
//...
}
private static class Proxy implements com.utte.aidltest.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 getBookList() throws android.os.RemoteException {
//...
}
@Override
public void addBook(com.utte.aidltest.Book book) throws android.os.RemoteException {
//...
}
}
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.utte.aidltest.Book book) throws android.os.RemoteException;
}
来看我画了好久的图:
IBookManager:
- IBookManager继承了IInterface接口。
所有需要在Binder中运输的接口都需要继承IInterface。
- 声明了两个抽象方法getBookList()和addBook()。
- 还定义了一个静态内部抽象类Stub。
Stub:
- Stub继承Binder、实现IBookManager,是个抽象类。
Stub其实就是Binder类。
- Stub定义了一个静态方法asInterface。
- 实现了IInterface的asBinder()。
- 实现了Binder的onTransact()。
- 定义了一个静态内部类Proxy。
- 声明了两个整型id。
这两个id用于标识IBookManager中的两个抽象方法,用于区分transact过程中客户端具体请求的是哪个方法。
Proxy:
- Proxy真正实现了最外层的IBookManager。
Stub的内部代理,主要控制是否需要跨进程transact的逻辑。
- 定义了getInterfaceDescriptor()。
- 实现了IInterface的asBinder()。
- 实现了IBookManager定义的抽象方法getBookList()和addBook()。
下面来看这里两个类中成员的具体含义和细节。
DESCRIPTOR
Binder的唯一标识,一般用Binder的类名表示。
private static final java.lang.String DESCRIPTOR = "com.utte.aidltest.IBookManager";
asInterface()
用于将服务端中的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端服务端位于同一进程,那么此方法返回的是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。
public static com.utte.aidltest.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.utte.aidltest.IBookManager))) {
return ((com.utte.aidltest.IBookManager) iin);
}
return new com.utte.aidltest.IBookManager.Stub.Proxy(obj);
}
asBinder()
返回当前Binder对象。
- Stub asBinder():返回当前对象。
@Override
public android.os.IBinder asBinder() {
return this;
}
- Proxy asBinder():返回构造器传入的Binder对象。
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
onTransact()
这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。具体流程:
- 服务端通过code来判断客户端请求的目标方法是什么。
- data装着目标方法的参数,将参数取出。
- 传入参数,调用执行code所代表的方法。
- 执行对应方法完毕后,向reply中写入目标方法的返回值。
- 如果onTransact()返回false,那么客户端会请求失败。
@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.utte.aidltest.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.utte.aidltest.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
Proxy#getBookList()
这个方法运行在客户端。客户端远程调用此方法时,具体流程如下:
- 创建方法所需要的Parcel型的_data和_reply,再创建真正的返回值对象_result。
- 把方法参数写入_data(如果有参数)。
- 调用transact()来发起远程过程调用(RPC)请求,当前线程挂起。
- 服务端onTransact()被调用。
- RPC返回,当前线程继续执行,从_reply中取出返回结果构造_result。
- 回收_data和_reply,返回_result。
@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.utte.aidltest.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
Proxy#addBook()
和Proxy#getBookList()基本一致,只不过没有返回值,不需要从_reply中取出返回值。
@Override
public void addBook(com.utte.aidltest.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();
}
}
注意点与总结
- 远程请求是耗时的。客户端发起请求时,当前线程会被挂起直到服务端返回数据,所以不能再UI线程进行远程请求。
- 服务端的onTransact()运行在Binder线程池中,所以此Binder方法是否耗时,都应该采用同步方式去实现,因为它已经运行在一个单独线程中了。
- 如果因为某些原因,服务端进程异常终止,如果我们不知道连接已经断裂,那么客户端就会受到影响。应该通过Binder的linkToDeath和unlinkToDeath设置死亡代理。
- Binder工作机制图:
自己实现Binder
不依赖AIDL,自己可以根据上面的分析来实现一个Binder。
- Cat类
- ICatManager 接口
- CatManagerStub Binder类