在基础使用篇我们在service中定义了一个内部类继承自Binder,然后在onBind()方法中返回一个该类的实例,以此来实现服务与活动之间的通信。但是,这种方式只能在应用程序自身内部进行通信,不能支持跨进程通信。那如果需要用到跨进程通信,怎么办呢?可以通过Messenger和AIDL。本篇将介绍Messenger在跨进程通信中的使用。
Messenger,通俗讲就是信使,带信的哥们。它是一种轻量级的IPC方案,底层实现其实就是AIDL。
一、特点:
- 串行处理客户端发送过来的消息;
- 主要用于传递消息
二、支持的数据类型:
Messenger是使用Message来进行消息传递的,所以基本上可以说Message支持的消息类型就是Messenger所支持的类型了。为什么说基本上呢?肯定是有原因的,就是Message的object字段比较特殊。这里借用任玉刚在《Android开发艺术探索》第二章中的一段话:
简单来说,Message中所支持的数据类型就是Messenger所支持的传输类型。实际上,通过Messenger来传输Message,Message中能使用的载体只有what、arg1、arg2、Bundle以及replyTo。Message中的另一个字段object在同一个进程中是很实用的,但是在进程间通信的时候,在Android 2.2以前object字段不支持跨进程传输,即便是2.2以后,也仅仅是系统提供的实现了Parcelable接口的对象才能通过它来传输。这就意味着我们自定义的Parcelable对象是无法通过object字段来传输的。
三、具体使用
1、单向通信(客户端->服务端)
服务端
步骤如下:
- 新建一个项目,在项目中新建一个MessengerService类继承自Service;
- 在MessengerService类的内部定义一个内部类MessengerServiceHandler继承自Handler,根据具体业务需求重写handleMessage()方法;
- 通过传入一个MessengerServiceHandler的实例来创建Messenger对象;
- 在onBind()方法中,将Messenger对象的Binder字段返回;
具体代码如下
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
//通过传入一个MessengerServiceHandler实例来创建一个Messenger对象
private Messenger messenger = new Messenger(new MessengerServiceHandler());
public MessengerService() {
}
@Override
public IBinder onBind(Intent intent) {
//返回Messenger的Binder字段给客户端
return messenger.getBinder();
}
//定义一个MessengerServiceHandler继承自Handler,并重新handleMessage()方法
public static class MessengerServiceHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1: //这里的1主要起消息动作区分作用,需要自己在客户端和服务端对应定义好,通俗来讲就是要知道1的时候是干嘛,2的时候是干嘛
//这里只是做演示,所以只是直接打印客户端传递过来的数据
Log.d(TAG, "MessengerService receive a message:" + msg.getData().getString("hello"));
break;
default:
break;
}
}
}
}
记得在AndroidManifest.xml中注册
<service
android:name=".MessengerService"
android:enabled="true"
android:exported="true">
<!--这里intent-filter用于隐式启动,如果是显示启动,则可以不写-->
<intent-filter>
<action android:name="com.czh.messengerservice.MessengerService"></action>
</intent-filter>
</service>
客户端
同样,因为是要验证进程间通信,所以我们重新创建一个项目,在MainAcitvity中编写代码,主要步骤为:
- 通过客户端与服务端的绑定,在onServiceConnected()中获取到IBinder实例,利用该实例创建出Messenger对象;
- 调用Messenger对象的send()方法将消息发送给服务端;
代码如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MessengerClient";
private Button btn_bind;
private Button btn_unbind;
private Button btn_sayhello;
private Messenger serviceMessenger;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//在绑定成功后拿到IBinder的实例,并通过该实例来创建用来与服务端通信的serviceMessenger
serviceMessenger = new Messenger(iBinder);
Log.d(TAG, "绑定到服务端成功");
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
serviceMessenger = null;
Log.d(TAG, "连接异常断开");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_bind = findViewById(R.id.btn_bind);
btn_unbind = findViewById(R.id.btn_unbind);
btn_sayhello = findViewById(R.id.btn_sayhello);
btn_bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//这里用的是显示启动
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.czh.messengerservice", "com.czh.messengerservice.MessengerService");
intent.setComponent(componentName);
//service隐式启动,5.0之后必须指定包名,否则报错
//远程服务的service需要在intentFilter中添加对应action
//intent.setAction("com.czh.messengerservice.MessengerService");
//intent.setPackage("com.czh.messengerservice");
bindService(intent, conn, BIND_AUTO_CREATE);
}
});
btn_unbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (serviceMessenger != null) {
serviceMessenger = null;
unbindService(conn);
Log.d(TAG, "解除绑定");
}
}
});
btn_sayhello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (serviceMessenger != null) {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("hello", "hello, service!");
message.setData(bundle);
message.what = 1;//用作标识,根据服务端定义,用来区分要做什么事情,下同
try {
//将消息发送给服务端
serviceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Log.d(TAG, "请先绑定服务");
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (serviceMessenger != null) {
serviceMessenger = null;
unbindService(conn);
Log.d(TAG, "解除绑定");
}
}
}
可以看到,我们再客户端应用放置了3个按钮,分别用来绑定、解绑和打招呼,启动两个应用程序,分别点击绑定、打招呼、解绑。
客户端Log如下:
服务端Log如下:
这样,我们就实现了简单的从客户端到服务端的跨进程通信了。
2、双向通信(客户端<->服务端)
有时候,我们不仅需要服务端能够接收到客户端发送过来的消息,还需要服务端能够发送消息给客户端,怎么搞?
让我们思考一下,客户端能给服务端发消息,靠的就是服务端派过来的Messenger对象(确切地说,是利用从服务端传递过来的IBinder实例创建出的Messenger对象)。那我们服务端要给客户端发消息,需要的就是客户端给服务端一个信使(Messenger对象),这样,通过这个信使,服务端就能给客户端发消息了。
下面请看具体的代码改造:
客户端
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MessengerClient";
private Button btn_bind;
private Button btn_unbind;
private Button btn_sayhello;
private Button btn_doplus;
private Messenger serviceMessenger;
//新增
//我们需要在客户端也定义一个类继承自Handler,利用该类的实例来创建客户端的Messenger实例
public static class MessengerClientHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
Log.d(TAG, "result:" + msg.getData().getInt("result"));
break;
default:
break;
}
}
}
//新增
//通过MessengerClientHandler实例来创建clientMessenger对象
private Messenger clientMessenger = new Messenger(new MessengerClientHandler());
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
serviceMessenger = new Messenger(iBinder);
Log.d(TAG, "绑定到服务端成功");
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
serviceMessenger = null;
Log.d(TAG, "连接异常断开");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_bind = findViewById(R.id.btn_bind);
btn_unbind = findViewById(R.id.btn_unbind);
btn_sayhello = findViewById(R.id.btn_sayhello);
btn_doplus = findViewById(R.id.btn_doplus);
btn_bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//这里用的是显示启动
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.czh.messengerservice", "com.czh.messengerservice.MessengerService");
intent.setComponent(componentName);
//service隐式启动,5.0之后必须指定包名,否则报错
//远程服务的service需要在intentFilter中添加对应action
//intent.setAction("com.czh.messengerservice.MessengerService");
//intent.setPackage("com.czh.messengerservice");
bindService(intent, conn, BIND_AUTO_CREATE);
}
});
btn_unbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (serviceMessenger != null) {
serviceMessenger = null;
unbindService(conn);
Log.d(TAG, "解除绑定");
}
}
});
btn_sayhello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (serviceMessenger != null) {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("hello", "hello, service!");
message.setData(bundle);
message.what = 1;//用作标识,根据服务端定义,用来区分要做什么事情,下同
try {
serviceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Log.d(TAG, "请先绑定服务");
}
}
});
//新增
//这里做了个示例,让客户端发送消息给服务端,服务端做加法运算后将结果返回给客户端
btn_doplus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (serviceMessenger != null) {
Message message = Message.obtain();
message.what = 2;
message.arg1 = 1;
message.arg2 = 2;
//注意这里,这里需要将客户端的Messenger存在消息中,在服务端将结果返回给客户端的时候要用到。
message.replyTo = clientMessenger;
try {
serviceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Log.d(TAG, "请先绑定服务");
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (serviceMessenger != null) {
serviceMessenger = null;
unbindService(conn);
Log.d(TAG, "解除绑定");
}
}
}
接下来我们来看一下服务端代码有哪些改动
服务端
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private Messenger messenger = new Messenger(new MessengerServiceHandler());
public MessengerService() {
}
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
public static class MessengerServiceHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
Log.d(TAG, "MessengerService receive a message:" + msg.getData().getString("hello"));
break;
//新增
//这里就是加法运算了
case 2:
int a = msg.arg1;
int b = msg.arg2;
int result = a + b;
//做完加法运算后,从消息缓存池中拿到一个将要发往客户端的Message对象
Message message = Message.obtain();
message.what = 1; //这里根据你自己的业务逻辑定义好就可以,用于在接收端收到信息后方便进行分类处理
Bundle bundle = new Bundle();
bundle.putInt("result", result);
message.setData(bundle);
//从消息中拿出客户端的信使
Messenger clientMessenger = msg.replyTo;
try {
//通过客户端的信使将消息发给客户端
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
}
再次启动两个应用程序,在客户端分别点击绑定,打招呼,做加法运算,解除绑定。
客户端Log如下:
服务端Log如下:
好了,愉快地玩耍吧!