Android Binder通信研习录
Binder作为Android进程间通信最重要的工具,一直以来困扰着我,这两天翻看了几位大神的博客,基本上都是从底层+应用层完全讲解,奈何本人对Jni不是很熟悉,所以忽略了底层的原理解析,现将应用层的原理整理下来,方便日后查看,如果有什么地方不准确,欢迎阅览者指出。
相关概念解析
- IInterface:系统提供的一个接口,所有可以在Binder中传输的接口都必须继承该接口;
- AIDL文件:通过该文件,可以自动生成一个固定格式的Binder类,可以理解为系统为我们提供的实现Binder的工具;
实现过程
既然AIDL文件能够自动生成一个Binder类,那么我们就先获得一个Binder对象;
- 准备Book.java文件,实现Parcelable接口:
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
protected 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 dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}
- 准备一个AIDL文件Book.aidl,用于声明经过序列化的Book.java:
package heyha.nj.com.binder;
import statements
parcelable Book;
- 编写AIDL接口文件IBookManager.aidl,注意要导入相关类:
import heyha.nj.com.tablayout.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
至此,编译程序,就会自动生成相应的Binder类,如下:
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements heyha.nj.com.tablayout.IBookManager {
private static final java.lang.String DESCRIPTOR = "heyha.nj.com.tablayout.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an heyha.nj.com.tablayout.IBookManager interface,
* generating a proxy if needed.
*/
public static heyha.nj.com.tablayout.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof heyha.nj.com.tablayout.IBookManager))) {
return ((heyha.nj.com.tablayout.IBookManager) iin);
}
return new heyha.nj.com.tablayout.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<heyha.nj.com.tablayout.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
heyha.nj.com.tablayout.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = heyha.nj.com.tablayout.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 heyha.nj.com.tablayout.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<heyha.nj.com.tablayout.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<heyha.nj.com.tablayout.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(heyha.nj.com.tablayout.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(heyha.nj.com.tablayout.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<heyha.nj.com.tablayout.Book> getBookList() throws android.os.RemoteException;
public void addBook(heyha.nj.com.tablayout.Book book) throws android.os.RemoteException;
}
下面详细说明该类的内容:
首先,它声明了两个方法getBookList和addBook,显然这是我们在aidl文件中声明的方法,同时声明了两个id分别标志这两个方法,这两个id标识transact过程中客户端请求的是哪个方法;接着,它声明了一个内部类Stub,这个Stub实现了它本身,所以Stub也是一个Binder类;根据asInterface方法的实现,如果客户端和服务端位于一个进程中,那该方法返回的是它本身(IBookManager),否则返回的是Stub的内部类Proxy,据此可知,如果客户端跨进程访问,那么方法调用是通过Proxy实现的;那么这个类内部的每个方法的含义是怎样的呢?
- asInterface(android.os.IBinder obj):用于将服务端返回的Binder对象转化为客户端需要的AIDL接口类型的对象;该方法区分进程,即客户端和服务端位于同一进程,返回服务端的Stub对象本身,否则返回经过系统封装的Stub.Proxy对象;
- asBinder():返回服务端的Stub对象;
- onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags):该方法位于服务端的Binder线程池中,用于处理客户端和服务端位于同一进程时的方法调用;如果返回false,那么客户端请求失败;
- Proxy.getBookList():该方法中的mRemote.transact()位于服务端的Binder线程池中用于处理客户端和服务端位于不同进程时的方法调用,注意,调用该方法的客户端和服务端是同步的,所以不能在UI线程中调用该方法;
至此,Binder的应用层实现完成;但是,如果服务端由于某种原因关闭,Binder连接断裂,那么客户端调用方法就会失败,客户端如何在Binder连接断裂时接到通知然后重新发起请求呢?好在Binder提供了linkToDeath方法和unlinkToDeath方法,在客户端为Binder设置linkToDeath方法就可以在Binder断裂时接到通知,重新发起请求需要自己去实现。