关于binder
binder是安卓中跨进程通信的一种方式,是安卓系统通信机制中及其重要的一环,很多系统调用比如AMS,SM,都是通过binder实现的。
为什么是binder
在Linux中有Socket,管道等跨进程通信方式,为什么还要binder来完成IPC呢?
出于效率和安全。首先,binder效率较Socket来说相对要高。其次,socket只能在上层协议进行验证操作(应用id在数据报中传输,容易被修改),ip和端口都可以手动输入来连接服务端。binder在内核为传输安全提供了支持,可以自动为发送方添加应用id/进程id。
binder和AIDL
在安卓中进行进程间通讯我们一般选择AIDL。首先创建对应的aidl接口,声明远程方法。如果涉及对象作为参数,则该对象要实现Pracelable接口。并且对该类要单独声明对应的aidl文件。完成一系列声明后,编译项目则可用自动生成的对应类进行进程间通讯了。
仔细观察/build/generated/aidl/ 目录下生成的对应类,会发现其也是利用binder相关类来实现的。这里引出了一个问题:AIDL在安卓进程间通讯扮演什么角色。
其实我们把自动生成的类copy一份,然后删除aidl文件,会发现通讯能正常进行。因此可以得出结论:
aidl并不是进程间通讯所必须,而是作为一个辅助工具,帮我们快速构建进程通讯相关代码。
对binder的总结
要了解binder及其原理,我倾向从三个方面入手(参考这篇博客):
1.binder怎么知道要调哪个进程的哪个方法。
2.怎么传递参数。
3.怎么优雅的实现以上两点。
事实上,在binder跨进程通信中存在四个角色。分别是 客户端(client),服务端(server),服务管理器(Server Manager),binder驱动。
服务端会先向服务管理器注册自己,也就是ServerManager上会先存储Server相关的信息,当客户端调用对应方法时,请求的实际上是binder驱动,由binder驱动去查阅哪个进程注册了对应的信息,然后告诉Server去调用对应的方法,结果同样由binder驱动返回。这之间的参数传递由序列化(Parcelable)实现。
源码分析
所谓进程间通讯,无非就是值的传递。在binder中通过方法调用的参数传递和返回值来完成。
既然是方法调用,首先需要定义行为。有哪些可以调用的方法,例如:
public interface mInterfaceImp extends IInterface {
public static final int FUN_ADD_BOOK = 101;
public static final int FUN_GET_BOOK_LIST = 102;
public static final int REGISTER_LISTENER = 103;
public static final int UNREGISTER_LISTENER = 104;
...
void addBook(Book book);
List<Book> getBookList();
void registerListener(RegisterListener listener);
void unRegisterListener(RegisterListener listener);
}
定义四个整形常量是为了表示四个方法,通过服务端和客户端协商,某个具体的数字(例如101)可以代表一个具体的方法(addBook)。继承IInterface接口是为了使该接口具备化身binder的能力(并不真是binder,由子类实现)。这个后面会讲。
定义完行为后就需要服务端和客户端都具有这些共同的行为,并且这些行为是可以跨进程交流的。怎么做呢,用binder。
Binder实现了IBinder接口,在内核有binder驱动支持,可以进行进程间通讯。在这里可以理解成IBinder代表进程间通讯的能力。
试着用一个binder的子类来实现我们上面定义的接口,使其具有我们想要的行为。也就是说,这些行为有了进程间通信的能力。
public static abstract class Stub extends Binder implements mInterfaceImp {
private static final java.lang.String DESCRIPTOR = "aidl.lInterface";
public Stub(){
this.attachInterface(this,DESCRIPTOR);//注册binder,注册后,queryLocalInterface方法将会返回当前binder
}
@Override
public IBinder asBinder(){//化身为binder的能力(自己就是)
return this;
}
//该方法由binder驱动调用,在这里通过与客户端协商好的id来分辨客户端要调哪个方法。参数通过Parcelable传递
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case FUN_ADD_BOOK:
data.enforceInterface(DESCRIPTOR);
Book book;
if(0!=data.readInt()){
book = Book.CREATOR.createFromParcel(data);
}else {
book = null;
}
reply.writeNoException();
addBook(book);
return true;
case FUN_GET_BOOK_LIST:
data.enforceInterface(DESCRIPTOR);
reply.writeNoException();
reply.writeTypedList(getBookList());
return true;
case REGISTER_LISTENER:
data.enforceInterface(DESCRIPTOR);
IBinder iBinder = data.readStrongBinder();
RegisterListener registerListener = RegisterListener.Stub.asInterface(iBinder);
this.registerListener(registerListener);
reply.writeNoException();
break;
case UNREGISTER_LISTENER:
break;
}
return super.onTransact(code, data, reply, flags);
}
//静态方法,用于判断当前所在进程是否和当前binder在同一进程,见上面的构造方法,如果在同一进程,那么queryLocalInterface应该有值,反之代表不在同一进程,返回一个代理类。
public static mInterfaceImp asInterface(IBinder iBinder){
if(iBinder==null){
return null;
}
IInterface mInterfaceImp = (IInterface) iBinder.queryLocalInterface(DESCRIPTOR);
if(mInterfaceImp!=null&&mInterfaceImp instanceof mInterfaceImp){
return (com.example.administrator.animdemo.mInterfaceImp) mInterfaceImp;
}else {
return new Proxy(iBinder);
}
}
}
可以看到该类并没有真正实现我们所定义的方法,而是声明为一个抽象类。这是因为我们并不知道使用者具体要怎么使用这些方法,所以把这些方法的具体实现交给使用者。
asInterface中返回的代理类是这样的:
private static class Proxy extends Binder implements mInterfaceImp{
private IBinder binder;
public Proxy(IBinder binder){
if(binder == null){
throw new RuntimeException("binder couldn't be null");
}
this.binder = binder;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void addBook(Book book) {
Parcel parcel = Parcel.obtain();
Parcel reply = Parcel.obtain();
try{
parcel.writeInterfaceToken(DESCRIPTOR);
if(book!=null){
parcel.writeInt(1);
book.writeToParcel(parcel,0);
}else {
parcel.writeInt(0);
}
binder.transact(FUN_ADD_BOOK,parcel,reply,0);
reply.readException();
} catch (RemoteException e) {
e.printStackTrace();
} finally {
parcel.recycle();
reply.recycle();
}
}
@Override
public List<Book> getBookList() {
Parcel parcel = Parcel.obtain();
Parcel reply = Parcel.obtain();
List<Book> list = null;
try {
parcel.writeInterfaceToken(DESCRIPTOR);
binder.transact(FUN_GET_BOOK_LIST,parcel,reply,0);
reply.readException();
list = reply.createTypedArrayList(Book.CREATOR);
} catch (RemoteException e) {
e.printStackTrace();
}finally {
parcel.recycle();
reply.recycle();
}
return list;
}
@Override
public void registerListener(RegisterListener listener) {
Parcel obtain = Parcel.obtain();
Parcel reply = Parcel.obtain();
try{
obtain.writeInterfaceToken(DESCRIPTOR);
obtain.writeStrongBinder(listener.asBinder());
try {
binder.transact(REGISTER_LISTENER,obtain,reply,0);
} catch (RemoteException e) {
e.printStackTrace();
}
}finally {
obtain.recycle();
reply.recycle();
}
}
@Override
public void unRegisterListener(RegisterListener listener) {
Parcel obtain = Parcel.obtain();
Parcel reply = Parcel.obtain();
try{
obtain.writeInterfaceToken(DESCRIPTOR);
obtain.writeStrongBinder(listener.asBinder());
try {
binder.transact(REGISTER_LISTENER,obtain,reply,0);
} catch (RemoteException e) {
e.printStackTrace();
}
}finally {
obtain.recycle();
reply.recycle();
}
}
@Override
public IBinder asBinder() {
return binder;
}
}
代理类的方法都有具体实现,因此适合用于客户端,在具体实现中将参数序列化,然后通知binder驱动调用服务端的对应方法,也就是上面的onTransact方法。对于客户端来说,并不知道拿到的是真正的binder还是被代理的binder,他只关心调对应的方法,真正的跨进程有binder驱动来关心。
当aidl接口作为方法的参数传递时,情况得到了反转,客户端和服务端的角色互换了。例如要实现跨进程的观察者模式。
public interface RegisterListener extends IInterface {
...
void newBookArrived(Book book) throws RemoteException;
}
先定义接口
RegisterListener.Stub registerListener = new RegisterListener.Stub() {
@Override
public void newBookArrived(Book book) throws RemoteException {
Log.e("-------", "------MainActivity.java:87------newBookArrived()------ " +
""+book);
}
};
创建观察者,或者说创建binder。注意,这里的binder是作为服务端存在。
@Override
public void registerListener(RegisterListener listener) {
Parcel obtain = Parcel.obtain();
Parcel reply = Parcel.obtain();
try{
obtain.writeInterfaceToken(DESCRIPTOR);
obtain.writeStrongBinder(listener.asBinder());//这里形参是RegisterListener,而RegisterListener实现了IInterface,因此具有binder化的能力(实际类型其实是binder)
try {
binder.transact(REGISTER_LISTENER,obtain,reply,0);
} catch (RemoteException e) {
e.printStackTrace();
}
}finally {
obtain.recycle();
reply.recycle();
}
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
...
case REGISTER_LISTENER:
data.enforceInterface(DESCRIPTOR);
IBinder iBinder = data.readStrongBinder();
RegisterListener registerListener = RegisterListener.Stub.asInterface(iBinder);//相当于客户端,判断是否和服务端在统一进程,否则拿到代理对象。
this.registerListener(registerListener);
reply.writeNoException();
break;
case UNREGISTER_LISTENER:
break;
}
return super.onTransact(code, data, reply, flags);
}
调用者对应的代理实现和onTransact方法。