Binder工作在linux层,是一个不需要硬件的驱动,从线程角度看,Binder代码运行在内核态,客户端程序使用Binder是通过系统调用完成的。
Binder是一种整体架构,包含服务端接口,Binder驱动,客户端接口三个模块。
1. 服务端
一个Binder服务端实际上就是一个Binder类的对象,该对象一旦创建,内部就启动一个隐藏线程。该线程会接收Binder驱动发送的消息,收到消息后,会执行到Binder对象的onTransact()函数,并按照该函数的参数执行不同的服务代码。
因此,要实现一个Binder服务,就必须重载onTransact()方法。onTransact()函数的主要工作就是把onTransact函数的参数转换成服务函数的参数。onTransact()参数的来源是客户端调用transact()函数时输入的,因此,若transact()有固定格式的输入,那么onTransact()就有固定格式的输出。
2. Binder驱动
Binder驱动是Android在内核中为完成进程间通信而专门设置的一个虚拟设备/dev/binder。任意一个服务端Binder对象被创建时,同时会在Binder驱动中创建一个mRemote对象,该对象的类型也是Binder类。客户端药访问远程服务时,都是通过mRemote对象。mRemote对象也重载了transact()方法,重载的内容主要包括:
- 以线程间消息通信的模式,向服务端发送客户端传递过来的参数。
- 挂起当前线程,当前线程正是客户发送线程,并等待服务端线程执行完指定服务函数后通知。
- 接收服务端程序的通知,然后继续执行客户端线程,并返回到客户端代码区。
3. 客户端(应用程序端)
客户端要想访问远程服务,必须获取远程服务在Binder对象中的mRemote引用。客户端获得mRemote后,就可以直接调用其transact方法了。
4. Binder例子
(1) Service端实现
只要基于Binder类新建一个Service类,然后重载onTransace()函数就可以了。
public class MusicPlayerService extends Binder {
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
switch (code) { // code用于标识客户端期望调用服务端的哪个函数,由双方约定。该值与客户端的transact()函数中的第一个参数code的值是一致的(可以通过SDK中的aidl工具实现)。
case PLAY: {
data.enforceInterface("MusicPlayerService"); // 为了某种校验,它与客户端的writeInterfaceToKen()对应。
String filePath = data.readString(); // 从客户端读出传递过来的参数。参数在data变量中的位置需要客户端和服务端双方约定。
start(filePath);
}
break;
default:
break;
}
}
public void start(String filePath) {
}
public void stop() {
}
}
(2) 客户端实现
服务分为系统服务和客户端服务。系统服务指可以通过getSystemService()来获取的服务,客户端服务指应用程序提供的自定义服务。
客户端如何获取Binder对象?
AmS提供了startService()函数用于启动客户端服务。对于客户端来说,可以使用以下两个函数来和一个服务建立连接:
i) public ComponentName startService(Intent intent);
该函数用于启动intent指定的服务,服务启动后,客户端暂时还没有服务端的Binder引用,因此此时还不能调用任何服务的功能。
ii) public boolean bindService(Intent Service, ServiceConnection conn, int flags);
该函数用于绑定一个服务,第二个参数是interface类,它的定义为:
public interface ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponentName name);
}
当客户端请求AmS启动某个Service之后,该Service如果正常启动,那么AmS会远程调用ActivityThread类中的ApplicationThread对象,调用的参数中会包含Service的Binder引用,然后在ApplicationThread中会回调bindService的conn接口。
因此,客户端可以在onServiceConnected方法中哦功能将其中的参数service保存为一个全局变量,从而在客户端的任何地方都可有随时调用该远程服务了。