Android中的IPC方式(一)

使用Bundle

四大组件中的三大组件(Activity、Service、BoardcastReceiver)都支持在Intent中传递Bundle数据。Bundle实现了Parcelable接口,可以很方便地在不同进程间传输。

我们可以在Bundle附加要传输给其他进程的信息并通过Intent发送给其他进程。注意的是传输的数据必须是可以被序列化的,例如基本类型、实现了Serializable或Parcelable接口的对象以及一些Android支持的特殊对象。

使用文件共享

两个进程通过读或写同一个文件来交换数据。除了可以交换文本信息,还可以序列化一个对象到文件并在另一个进程中反序列化这个对象。

但是这种方式在并发的时候可能会出问题。所以文件共享适合在并发不高的进程间通信,且要处理好并发问题。

使用Messenger(不是Message)

Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。Messenger对象可以在不同进程中传递,通过在Messenger中放入需要传递的数据,就可以进行简单的跨进程通信。

简单例子

假设有两个进程,一个客户端进程和一个服务端进程。项目结构如下:

其中MainActivity运行在客户端进程,而MessengerService运行在服务端进程。

1
2
3
<service
android:name=".MessengerService"
android:process=":remote"/>

客户端向服务端发送一条消息

客户端先绑定服务端的Service

1
2
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);

其中ServiceConnection对象的创建如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Messenger messenger = new Messenger(service);
//创建一个Message并设置其what值为Constant.MSG_FROM_CLIENT
Message message = Message.obtain(null, Constant.MSG_FROM_CLIENT);
//通过给Message设置Bundle来携带传输的数据
Bundle bundle = new Bundle();
bundle.putString(Constant.KEY_MSG, "One Msg from client.");
message.setData(bundle);
//通过Messenger发送Message
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};

客户端绑定服务端的Service后:

  1. 根据服务端返回的IBinder对象创建一个Messenger
  2. 创建一个Message来携带要传输的消息
  3. 通过Messenger将Message发送给服务端

服务端的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MessengerService extends Service {

public static final String TAG = "fzh";

//通过Handler来创建Messenger对象,Messenger将其他
// 进程(客户端)发来的消息传递给Handler来处理
private final Messenger mMessenger = new Messenger(new MessengerHandler());

//Handler处理其他进程发来的消息
//这里将消息打印出来
static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constant.MSG_FROM_CLIENT:
Log.d(TAG, "MessengerService: " + msg.getData()
.getString(Constant.KEY_MSG));
break;
default:
break;
}
}
}

@Override
public IBinder onBind(Intent intent) {
//返回这个Messenger对象对应的底层Binder
return mMessenger.getBinder();
}
}

  1. 服务端先创建一个Handler来处理其他进程发来的message
  2. 通过刚才的Handler来创建Messenger对象,Messenger将其他进程传来的message交给这个Handler来处理
  3. 在onBind方法中返回这个Messenger对象对应的底层Binder,服务端和客户端就是通过这个Binder联系起来

最后运行程序,打印结果如下:

可以看到,远程的服务端收到了消息。

服务端回应客户端

我们已经知道客户端如何发送消息给跨进程的服务端,现在来看下服务端在收到消息后,怎样回应客户端。

客户端先创建一个Messenger,用于接收服务端的回应,这个Messenger也是通过Handler来创建,最终在Handler中接收服务端的回应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constant.MSG_FROM_SERVICE:
Log.d(TAG, "Receive message from service: " + msg.getData()
.getString(Constant.KEY_REPLY_MSG));
break;
default:
break;
}
}
}

然后将发送给服务端的message的replyTo参数设置为刚才创建的Messenger,这样就可以将这个Messenger发送给服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//...

message.replyTo = mGetReplyMessenger;

//通过Messenger发送Message
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};

服务端在接收完消息后,通过message的reply参数获得了客户端的Messenger。然后就可以通过这个Messenger来回应客户端,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constant.MSG_FROM_CLIENT:
Log.d(TAG, "MessengerService: " + msg.getData()
.getString(Constant.KEY_MSG));

//服务端通过message的replyTo参数回应客户端
Messenger messenger = msg.replyTo;
//回应给客户端的message
Message replyMessage = Message.obtain(null, Constant.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString(Constant.KEY_REPLY_MSG, "已收到消息");
replyMessage.setData(bundle);
//发送回应消息
try {
messenger.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}

运行程序,打印结果如下:

可以看到,客户端收到了服务端的回应。

小结

  1. 客户端向服务端发送消息的流程:客户端先绑定服务端的Service,绑定成功后,用服务端返回的IBinder对象创建一个Messenger,并用这个Messenger传递message给服务端。服务端收到message后就交给对应的Handler来处理。
  2. 服务端回应客户端的流程:客户端先创建一个Messenger,然后在传递message给服务端时,将这个Messenger赋给message的replyTo参数。服务端在收到消息后,通过message的replyTo参数得到客户端的Messenger,服务端的回应消息就可以通过这个Messenger发送的message传递过去,客户端就在这个Messenger对应的Handler中处理服务端回应的message。

Messenger的不足

  1. Messenger是以串行的方式处理消息,也就是说即使有大量的消息同时发生到服务端,服务端也只能一个个处理。所以如果有大量的并发请求,就不适合用Messenger了。
  2. Messenger只能用于传递消息,不能跨进程调用方法。

参考

  • 《Android 开发艺术探索》
-------------    本文到此结束  感谢您的阅读    -------------
0%