前言
在Android中实现IPC(进程间通信)
的方式有很多,本文讲解如何使用Messenger
进行实现。Messenger
可以翻译为信使,用于跨进程传输Message
对象。在Message
对象中,我们可以附带其他数据,这就实现了数据的跨进程传递。简而言之,Messenger
是一种轻量级的IPC
方案,在它的底层封装了AIDL
。
支持的数据类型
Messenger
只能传递Message
对象。因此,Message支持的数据类型就是Messenger支持的数据类型。Message
的arg1、arg2、what
字段各自可以附加一个int
型数据,而obj
字段可以附加一个Object
对象,但是只支持系统提供的Parcelable
对象。因此,传递数据的重任就落在了Bundle
身上。通过Bundle,我们可以传递以下类型的数据:
- 基本数据类型
String
和CharSequence
- 实现
Parcelable
接口的对象 - 实现
Serializable
接口的对象
基本流程
服务端进程
首先我们要创建一个Service
,用于提供服务。在Service
中,我们需要创建一个Handler
对象,用于接收客户端进程发来的数据。其次,通过这个Handler
创建一个Messenger
对象。在Messenger
对象中封装有Binder
。因此,我们在Service
的onBind
方法中将Messenger
对应的Binder
对象返回。
客户端进程
首先,我们需要绑定服务器端进程的Service
,并通过绑定后返回的IBinder
对象创建一个Messenger
。借助这个Messenger
对象,我们就可以向服务器端进程发送Message
数据了。
双向通信
通过上述步骤,我们只能做到从客户端进程向服务端进程传递数据,即单向通信
。如果同时还要求从服务器端进程向客户端进程传递数据,就需要在客户端进程也创建一个Handler
对象,用于接收服务器端传来的数据。此外,我们要通过这个Handler
创建一个Messenger
对象,并通过Message
的replyTo
字段传递给服务器端进程。服务器端可以从传来的Message
中取出这个Messenger
对象,然后就可以使用它向客户端传递数据了。这样,就实现了客户端和服务器端间的双向通信
。
实现单向通信
服务器端:
public class ServerService extends Service {
private static final String TAG="ServerService";
public static final int DATA_FROM_CLIENT=1;
//自定义Handler,用于接收客户端传来的数据
private static class ServerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DATA_FROM_CLIENT:
Bundle bundle=msg.getData();
Log.v(TAG,bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER));
break;
default:
break;
}
}
}
private Messenger messenger=new Messenger(new ServerHandler());
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();//返回Messenger对象底层封装的Binder
}
}
可以看到,我们在Service
中定义了一个静态内部类ServerHandler
。同时,我们通过以下代码创建了一个Messenger
对象:
private Messenger messenger=new Messenger(new ServerHandler());
在onBind
方法中,我们通过getBinder
方法返回了Messenger
封装的Binder
对象。
return messenger.getBinder();//返回Messenger对象底层封装的Binder
这样,客户端传递的数据就可以在ServerHandler
中处理了。在ServerHandler
中,我们通过以下代码取出Message
中的数据:
Bundle bundle=msg.getData();
bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER);
同时,我们为这个Service
指定一个私有进程名,这样就让这个Service
运行在独立的进程中了。
<service
android:name=".ServerService"
android:process=":remote">
</service>
客户端:
public class ClientActivity extends AppCompatActivity {
public static final String KEY_HELLO_TO_SERVER="HELLO_TO_SERVER";
private Messenger serverMessenger;//来自服务器端的Messenger
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serverMessenger=new Messenger(service);
Bundle bundle=new Bundle();
bundle.putString(KEY_HELLO_TO_SERVER,"你好,服务器~");
Message message=new Message();
message.what=ServerService.DATA_FROM_CLIENT;
message.setData(bundle);
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
Intent intent=new Intent(this,ServerService.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);//解绑
}
}
可以看到,我们通过匿名内部类的方式创建了一个ServiceConnection
对象。在它的onServiceConnected
方法中,我们通过获取到的IBinder
对象创建了一个Messenger
,通过这个Messenger
就可以向服务器端发送Message
了。
serverMessenger=new Messenger(service);
具体的发送代码如下:
Bundle bundle=new Bundle();
bundle.putString(KEY_HELLO_TO_SERVER,"你好,服务器~");
Message message=new Message();
message.what=ServerService.DATA_FROM_CLIENT;
message.setData(bundle);
serverMessenger.send(message);
我们先创建了一个Bundle
对象,并向其中存储了一个字符串。随后,我们创建了一个Message
对象,并将前面的Bundle
对象设置给Message
。最后,通过ServerMessenger
将Message
发送出去。
需要注意的是,一定要记得在Activity
的onDestroy
方法中解除对Service
的绑定。
unbindService(serviceConnection);//解绑
运行结果:
....../com.example.ipcdemo:remote V/ServerService: 你好,服务器~
实现双向通信
我们在上面实现了一个单向通信的例子。但是要做到从服务器端给客户端发送数据,还需要进行修改。主要改动在于客户端,我们需要提供一个Handler
和Messenger
。具体改动如下,在主要的新增代码前都添加了注释//Add
:
服务器端:
public class ServerService extends Service {
private static final String TAG="ServerService";
public static final int DATA_FROM_CLIENT=1;
public static final String KEY_HELLO_TO_CLIENT="HELLO_TO_CLIENT";
//自定义Handler,用于接收客户端传来的数据
private static class ServerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DATA_FROM_CLIENT:
Bundle bundle=msg.getData();
Log.v(TAG,bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER));
//Add
Messenger clientMessenger=msg.replyTo;
Bundle bundleToClient=new Bundle();
bundleToClient.putString(KEY_HELLO_TO_CLIENT,
"你好,客户端。我收到你的消息了。");
Message message=new Message();
message.what=ClientActivity.DATA_FROM_SERVER;
message.setData(bundleToClient);
try {
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
private Messenger messenger=new Messenger(new ServerHandler());
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();//返回Messenger对象底层封装的Binder
}
}
可以看到,在服务器端的主要改变是在Handler
中从Message
的replyTo
参数中获取了客户端提供的Messenger
对象。通过这个对象我们就可以向客户端发送Message
了。
Messenger clientMessenger=msg.replyTo;
客户端:
public class ClientActivity extends AppCompatActivity {
private static final String TAG="ClientActivity";
public static final String KEY_HELLO_TO_SERVER="HELLO_TO_SERVER";
public static final int DATA_FROM_SERVER=0;
private Messenger serverMessenger;//来自服务器端的Messenger
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serverMessenger=new Messenger(service);
Bundle bundle=new Bundle();
bundle.putString(KEY_HELLO_TO_SERVER,"你好,服务器~");
Message message=new Message();
message.what=ServerService.DATA_FROM_CLIENT;
message.setData(bundle);
//Add
message.replyTo=messenger;
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//Add
private static class ClientHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DATA_FROM_SERVER:
Bundle bundle=msg.getData();
Log.v(TAG,bundle.getString(ServerService.KEY_HELLO_TO_CLIENT));
break;
default:
break;
}
}
}
//Add
private Messenger messenger=new Messenger(new ClientHandler());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
Intent intent=new Intent(this,ServerService.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);//解绑
}
}
可以看到,我们定义了一个静态内部类作为客户端的Handler
类,并通过Handler
创建了一个Messenger
对象。这一过程和服务器端的做法一致。在绑定Service
时,我们将这个Messenger
作为Message
的replyTo
参数传递给了服务器端。这样,服务器端就可以获取到客户端提供的Messenger
了。具体代码如下:
message.replyTo=messenger;
运行结果:
....../com.example.ipcdemo:remote V/ServerService: 你好,服务器~
....../com.example.ipcdemo V/ClientActivity: 你好,客户端。我收到你的消息了。
小结
通过以上例子,可以看出Messenger
的使用是很方便的,毕竟系统已经为我们做了封装。但是,这种方式的局限性也很明显。首先,通过这种方式只能传递Message
,无法实现远程方法调用(RPC)
。其次,从上文的代码也可以看出Messenger
的消息传递是通过Handler
进行处理的,这意味着Messenger
的消息是串行处理的,显然这不适合高并发的场景。如果有更高的交互要求,可以考虑使用AIDL
实现IPC
。具体请参考另一篇博客: