Binder框架
Binder是一种架构,分为三个部分组成:
- 服务端接口
- Binder驱动
- 客户端接口
对于服务端,一个Binder服务端就是一个Binder类的对象,该对象一旦创建,内部就启动一个隐藏线程。该线程接下来会接收Binder驱动发送过来的消息,收到消息后,会执行到Binder对象中的onTransact
方法,根据按照不同的参数执行不同的服务端方法。
服务端重载onTransact
方法主要是把该方法的参数转化为服务端方法的参数,而该方法的参数来源于客户端调用transact
方法时的输入。所以,如果transact
有固有格式的输入,那么onTransact
就会有固定格式的输出。
对于Binder驱动,任意一个服务端Binder对象被创建时,同时会在Binder驱动中创建一个mRemote
对象,该对象的类型也是Binder。客户端调用远程服务时,调用的其实是mRemote
对象。在Binder驱动中,mRemote
被重载,主要做以下事情:
- 以线程间消息通信的模式,向服务端发送客户端传过来的参数
- 挂起当前线程(客户端线程),等待服务端线程执行完特定的服务端函数后通知
- 接收服务端线程的通知,然后继续执行客户端线程,返回客户端代码区
从上面的流程来看,对于客户端似乎是直接调用到了服务端的Binder,但实际上是通过Binder驱动进行了中转,即存在两个Binder对象。
服务端设计
对于服务端,需要做的事情是重写onTransact
方法:
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
}
其中的code
就是与客户端约定好的调用哪个方法的标识。比如约定当code
的值为100时,调用服务端的play
方法;data
和replay
分别是传递给方法的参数和方法的返回值,需要根据具体方法读取和写入。
客户端设计
对于客户端,第一步应该是从Binder驱动中获得与远程服务端对应的mRemote
引用。比如在Activity中通过调用bindService
,如果绑定服务成功,可以获得一个IBinder
对象,这个就是Binder驱动中的mRemote
。调用mRemote
引用的transact
方法,Binder驱动会挂起调用的当前客户端线程,然后向远程服务发送一个消息,该消息中包含了客户端传递的数据。服务端调用完具体的方法后将返回值放入reply
(如果有返回值的话),并向Binder发送一个notify
的消息,使得客户端线程重新被唤醒,完成调用。
AIDL生成的代码分析
从上文的分析可以看出,其实对于远程服务端的调用,都是基于约定的。比如对于code
、data
和reply
我们需要明确地赋予其意义。
-
code
的值对应哪个服务端方法,相应的data
中有哪些值、需要往reply
中写入哪些值 -
data
和replay
的数据写入顺序是怎样的
而AIDL工具就是帮我们处理这些繁琐事情的。以一个简单的AIDL分析:
// 文件名:IRemoteService.aidl
package com.demo;
interface IRemoteService {
void sayHello();
String getValue();
}
看起来和Java的接口定义差不多,只不过文件的拓展名是.aidl
不是.java
。
通过AIDL工具会在com.demo
包下生成一个IRemoteService.java
文件,这个才是给程序使用的。生成的文件主要包括三个部分的内容:
- 一个
IRemoteService
接口 - 一个
IRemoteService.Stub
的抽象类 - 在
IRemoteService.Stub
中定义一个Proxy
的内部类
下面具体分析:
// IRemoteService接口部分
package com.demo;
public interface IRemoteService extends android.os.IInterface {
public void sayHello() throws android.os.RemoteException;
public java.lang.String getValue() throws android.os.RemoteException;
}
这部分没什么好说的,就是将AIDL中的方法转化为Java接口的方法。
public static abstract class Stub extends android.os.Binder implements com.demo.IRemoteService {
// 规定code的值
static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
private static final java.lang.String DESCRIPTOR = "com.demo.IRemoteService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.demo.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.demo.IRemoteService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.demo.IRemoteService))) {
return ((com.demo.IRemoteService) iin);
}
return new com.demo.IRemoteService.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_sayHello: {
data.enforceInterface(DESCRIPTOR);
this.sayHello();
reply.writeNoException();
return true;
}
case TRANSACTION_getValue: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getValue();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.demo.IRemoteService {
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 void sayHello() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.lang.String getValue() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
代码看起来很长,但职责还是很明确的,一开始就规定了几个对应的int值,通过该值去映射到具体的服务端方法。
Stub
类继承了Binder
,重写了onTransact
方法,帮我们完成了code
值到对应方法的映射以及方法参数的读取与返回值的写入。Stub
implements了IRemoteService
,但并没有实现,显然这部分是让子类去实现的。
Proxy
类实现了IRemoteService
中的两个方法,方法中实现的数据写入和Stub
中onTransact
的数据读取是对应的,解决了数据顺序的问题。
对于服务端,我们只需要继承Stub
,然后实现IRemoteService
接口中的方法;对于客户端,我们拿到IBinder
后,可以通过Stub
的asInterface
方法,得到一个接口的实现类,其实也就是Proxy
的实例,之后就可以通过这个实例直接调用IRemoteService
的方法了。
小结:通过AIDL,帮我们生成的代码,我们就不用考虑transact
和onTransact
该如何实现,从而可以把精力放在具体的接口逻辑实现上。