Android Binder通讯机制
一、概念
从IPC的角度来说,Binder是android中一种跨进程的通讯方式,Binder可以理解为一种虚拟的物理设备,它的设备驱动是dev/binder;
从Android FrameWork角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,等)和相应ManagerService的桥梁;
从Android应用层来说,Binder是客户端和服务端通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端的服务和数据;
二、工作机制
通过一个AIDL示例来了解Binder的工作机制,新建Movie.java、Movie.aidl和IMovieMgr.aidl三个文件,代码如下:
// Movie.java
public class Movie implements Parcelable{
public String name;
public Movie(String name) {
this.name = name;
}
@Override public void writeToParcel(Parcel dest, int flags) {
}
@Override public int describeContents() {
return 0;
}
public static final Parcelable.Creator<Movie> CREATOR = new Parcelable.Creator<Movie>() {
@Override public Movie createFromParcel(Parcel source) {
return new Movie(source);
}
@Override public Movie[] newArray(int size) {
return new Movie[size];
}
};
private Movie(Parcel in) {
this.name = in.readString();
}
}
// Movie.aidl
package com.rico.aidl;
parcelable Movie;
// IMovieMgr.aidl
package com.rico.aidl;
// Declare any non-default types here with import statements
import com.rico.aidl.Movie;
interface IMovieMgr {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
List<Movie> getMovieList();
void addMovie(in Movie movie);
}
编译后系统自动生成build/generated/source/../IMovieMgr.java文件,手动格式化代码,通过IMovieMgr.java来了解下Binder的工作机制,代码如下:
package com.rico.aidl;
public interface IMovieMgr extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.rico.aidl.IMovieMgr {
private static final java.lang.String DESCRIPTOR = "com.rico.aidl.IMovieMgr";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.rico.aidl.IMovieMgr interface,
* generating a proxy if needed.
*/
public static com.rico.aidl.IMovieMgr asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.rico.aidl.IMovieMgr))) {
return ((com.rico.aidl.IMovieMgr) iin);
}
return new com.rico.aidl.IMovieMgr.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_addMovie: {
data.enforceInterface(DESCRIPTOR);
com.rico.aidl.Movie _arg0;
if ((0 != data.readInt())) {
_arg0 = com.rico.aidl.Movie.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addMovie(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.rico.aidl.IMovieMgr {
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<com.rico.aidl.Movie> getMovieList()
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.rico.aidl.Movie> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getMovieList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.rico.aidl.Movie.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addMovie(com.rico.aidl.Movie movie) 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 ((movie != null)) {
_data.writeInt(1);
movie.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addMovie, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getMovieList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addMovie = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
// 省略代码
public java.util.List<com.rico.aidl.Movie> getMovieList() throws android.os.RemoteException;
public void addMovie(com.rico.aidl.Movie movie) throws android.os.RemoteException;
}
首先IMovieMgr中声明了两个方法getMovieList和addMovie(这两个方法是我们在IMovieMgr.aidl中定义的),同时还声明了两个整型的id标识这两个方法,通过id来确定在transact过程中客户端请求的到底是哪个方法。
接着,它声明了一个Stub内部类,这个Stub就是Binder类,当客户端和服务端位于同一个进程,方法不会走跨进程的transact过程,而当位于不同进程,方法需要走跨进程transact,这个逻辑由Proxy来完成,下面详细介绍Stub和Proxy这两个类中每个方法的含义。
- asInterface(android.os.IBinder obj)
将服务端Binder对象转换成客户端需要的aidl接口类型对象,当客户端与服务端同进程,则返回Stub对象,不同进程则返回Stub.proxy对象 - asBinder
返回当前Binder对象 - onTransact(int code, Parcel data, Parcel reply, int flag)
该方法运行在服务端binder线程池中,客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理,服务端通过code来确定客户端具体调用了哪个方法,从data中获取参数,并将执行结果写入reply中,如果此方法返回false,那么客户端请求会失败,因此利用这个特性可以做接口访问权限验证。
需要注意的是,当客户端发起远程接口调用,当前线程挂起,直至服务端返回数据,所以不能再UI线程中调用远程接口,如果远程方法很耗时,会导致ANR,其次服务端binder方法运行在binder线程池中,所以binder方法需要采用同步的方式实现;
Binder工作机制: