在上篇文章《Android多进程通信概述》中,我们对Android中可用的多进程通信技术有一个总体的了解,知道多进程通信的重要性,本文就从最为常用也较为复杂的通信方式Binder开始,了解其机制及用法。
从Service说起
上篇文章提到,AIDL是Service跨进程通信的一种方式,因此也和Service有着不可分割的关系,Service中的 onBind() 方法可以使得Service与其他组件绑定和交互,该方法返回的 IBinder 对象定义了客户端用来与服务进行交互的编程接口,因此,创建提供绑定的服务时,必须提供 IBinder ,用以提供客户端用来和服务进行交互的编程接口。可以通过三种方式来定义接口:Binder,Messenger, AIDL。
扩展 Binder 类
若Service为只在本APP中使用,且与客户端运行在相同的进程下,则可以继承 Binder 类,使客户端通过该类访问服务端Service中的方法。具体步骤如下:
- 在Service中创建Binder扩展类的实例,该实例要满足下列其中一个条件:
- 包含客户端可调用的public方法
- 返回当前Service 实例,其中包含客户端可调用的public方法
- 返回Service包含的其他类的实例,该实例包含客户端可调动的public方法
IBinder localBinder = new LocalBinder();
public class LocalBinder extends Binder {
public LocalService getService() {
return LocalService.this;
}
}
- 在Service的onBind() 回调方法返回这个Binder扩展类的实例
@Nullable
@Override
public IBinder onBind(Intent intent) {
return localBinder;
}
- 在客户端中,从onServiceConnected() 回调方法中得到 Binder,并通过这个binder使用提供的方法绑定服务
LocalService.LocalBinder binder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
binder = (LocalService.LocalBinder) iBinder;
LocalService localService = binder.getService();
//得到Service实例localService,可以调用其中的方法
}
@Override
public void onServiceDisconnected(ComponentName name) {
binder = null;
}
};
要求服务和客户端必须在同一应用内,是为了便于客户端转换返回的对象和正确调用其 API。服务和客户端还必须在同一进程内,因为此方法不执行任何跨进程编组。
注:在OnBind()方法中需返回一个IBinder实例,不然onServiceConnected方法不会调用。
使用 Messenger
Messenger,信使,可以在不同的进程间传递message对象,在message中放入需要传递的数据就可以实现跨进程通信和传递数据。Messenger机制是基于消息的跨进程通信方式。 客户端和服务端可相互持有对方的Messenger来进行通信。
当需要进行进程间通信时,使用 Messenger 比使用 AIDL 更简单,因为 Messenger 会将所有Service调用排入队列,而 AIDL 会同时向 Service 发送多个请求,而Service则必须能够处理多线程。因此,Messenger适用于不需要处理多线程的Service,可以让 Service 一次处理一个调用。具体步骤如下:
- Service 实现一个 Handler,用来接收 client 每个调用的回调
private class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
//handle message
}
}
- Service 端用Handler实例创建Messenger对象
Messenger messenger = new Messenger(new IncomingHandler());
- Service 端用 Messenger 创建一个 IBinder,并通过 onBind() 返回到客户端
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
- Client 端用 IBinder实例化 Messenger(引用Service的Handler实例),并实例化Message发送给Service
private Messenger messenger;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
messenger = new Messenger(iBinder);
message = Message.obtain(null, MessengerService.MSG_ANSWER_NAME, 0, 0);
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
messenger = null;
}
};
- Service 端接收到 Client 发送来的 Message,并在 Handler中接收每个Message进行处理
private class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ANSWER_NAME:
//do something
break;
default:
super.handleMessage(msg);
}
}
}
Client与Server端通信
上述步骤可以实现将Server端对Client发送来的消息进行处理,但如果想让Clinet响应Service发送来的消息,也是可以实现的:在客户端实现一个Messenger,当客户端收到 onServiceConnected() 回调时,会向服务发送一条 Message,可以在该message中设置 replyTo 为客户端Messenger
客户端:
private Messenger localMessenger = new Messenger(new LocalHandler());
private Messenger messenger;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
messenger = new Messenger(iBinder);
message = Message.obtain(null, MessengerService.MSG_ANSWER_NAME, 0, 0);
message.replyTo = localMessenger;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
messenger = null;
localMessenger = null;
}
};
class LocalHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String result = msg.getData().getString("result");
switch (msg.what) {
case MessengerService.MSG_ANSWER_NAME:
//do something
break;
default:
super.handleMessage(msg);
}
}
}
服务端:
private class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ANSWER_NAME:
//do something
Message newMsg = Message.obtain();
Bundle bundle = new Bundle();
newMsg.what = MSG_ANSWER_NAME;
bundle.putString("result", result);
newMsg.setData(bundle);
try {
msg.replyTo.send(newMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
原理
而通过分析Messenger源码可以发现,其内部也是基于aidl文件的,具体分析由于篇幅限制就不再赘述了,其内部其实也是依赖一个aidl生成的类,这个aidl位于:frameworks/base/core/Java/android/os/IMessenger.aidl。也就是,我们代码中是:
Messenger mServiceMessenger = new Messenger(service);
而跟进去后会发现:
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
使用 AIDL
AIDL(Android 接口定义语言)执行将所有对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 IPC。之前采用 Messenger 的方法实际上是以 AIDL 作为其底层结构。Messenger 会在单一线程中创建包含所有客户端请求的队列,以便 Service 一次接收一个请求。 不过,若 Service 要同时处理多个请求,则可直接使用 AIDL,且服务必须具备多线程处理能力,并采用线程安全式设计。
如需直接使用 AIDL,需要创建一个定义编程接口的 .aidl 文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,就可以在 Service 内对其进行扩展。
小结
关于 AIDL 的介绍会在单独一篇博文里讲解,且以上关于Binder和Messenger的相关内容都有实现,见github代码,且该代码需要切换分支查看,分支分别为 service-binder
和 service-messenger