Binder是什么
Binder是Android中的一个类,它实现了IBinder接口。
应用层的角度在Service和其他组件绑定通信的时候就接触过,下面会从IPC角度和底层角度来分析。
Binder工作机制
SDK会自动生成Binder类
Book类
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@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 out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}
}
- getBookList:用于从远程服务端获取图书列表
- addBook:用于往图书列表中添加新的图书
建立AIDL
- Book.aidl
// Book.aidl
package com.lxy.bindertest;
parcelable Book;
- IBookManager.aidl
// IBookManager.aidl
package com.lxy.bindertest;
import com.lxy.bindertest.Book;
interface IBookManager{
List<Book> getBookList();
void addBook(in Book book);
}
在建立aidl的时候,可能找不到对应系统生成的.java文件,rebuild一下就好了。
分析IBookManager
先看下整体结构:
public interface IBookManager extends android.os.IInterface{
public static abstract class Stub extends Binder implements IBookManager{
private static final DESCRIPTOR = "com.lxy.bindertest.IBookManager";
public Stub(){
this.attachInterface(this,DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj){
、、、
}
public IBinder asBinder(){
return this;
}
public boolean onTransact(int code,Parcel data,Parcel reply,int flags) throws RemoteException{
、、、
}
private static class Proxy implements IBookManager{
private IBinder mRemote;
Proxy(IBinder remote){
mRemote = remote;
}
public IBinder asBinder{
return mRemote;
}
public String gerInterfaceDescriptor{
return DESCRIPTOR;
}
public List<Book> getBookList() throws RemoteException{
、、、
}
public void addBook(Book book) throws 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 List<Book> getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
}
这是把每个方法提出来的结构框图。。。
这里面最主要的就是Stub类和它的内部类Proxy
IBookManager
- 继承了IInterface接口,所有可以在Binder中传输的接口都需要继承IInterface接口。
- 声明了两个抽象方法getBookList和addBook
- 定义了静态内部类Stub
Stub
- Stub继承Binder、实现IBookManager,是个静态抽象类。
Stub就是一个Binder类
DESCRIPTOR
Binder的唯一标识。一般用当前的类名表示。
asInterface(androd.os.Binder obj)
将服务端的Binder对象转换成客户端所需的AIDL接口类型对象。但这种转换是区分进程的,当客户端和服务器端在同一进程中,此方法返回服务端的Stub对象本身,否则返回系统封装后的Stub.Proxy对象。
public static com.lxy.bindertest.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lxy.bindertest.IBookManager))) {
return ((com.lxy.bindertest.IBookManager) iin);
}
return new com.lxy.bindertest.IBookManager.Stub.Proxy(obj);
}
asBinder
返回当前Binder对象。
- Stub asBinder():返回当前对象
public android.os.IBinder asBinder() {
return this;
}
- Proxy asBinder():返回构造器传入的Binder对象
public android.os.IBinder asBinder() {
return mRemote;
}
onTransact
这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。具体流程:
- 服务端通过code确定客户端所请求的目标方法是什么
- data装着目标方法的参数,取出参数
- 传入参数,调用执行code对应的方法
- 目标方法执行完后,向reply中写入返回值
- 如果返回false,客户端请求失败
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<com.lxy.bindertest.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.lxy.bindertest.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lxy.bindertest.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
Proxy#getBookList
这个方法运行在客户端,当客户端远程调用此方法时,具体流程如下:
- 创建该方法所需的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象_result
- 把方法参数信息写入_data中
- 调用transact方法发起RPC(远程过程调用)请求,同时将线程挂起
- 服务端onTracsact()被调用
- RPC返回后,当前线程继续执行,从_reply中取出RPC过程的返回结果
- 返回_reply中的数据
public java.util.List<com.lxy.bindertest.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lxy.bindertest.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lxy.bindertest.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
Proxy#addBook
这个方法运行在客户端,执行过程和getBookList一样,addBook没有返回值,所以不需要从_reply中取出返回值。
注意与总结
- 如果远程方法耗时,则不能在UI线程中发起RPC。因为当客户端发起RPC时,当前线程会被挂起直到服务端返回数据。
- Binder方法不管是否耗时,都应该采用同步方式实现。因为Binder本来就运行在线程池中。
-
Binder工作机制图:
image
网友评论