Messenger 可以翻译为信使,顾名思义,通过它可以实现在不同进程中传递 Message 对象,在 Message 中可以携带我们需要传递的数据,借此就可以实现数据在不同进程间的通信。
Messenger 底层实现Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,这块同学们可以通过看 Messenger 这个类的源码中的构造方法得知。
Messenger 使用说明Messenger的使用会相对简单,它对 AIDL 做了封装,使得我们可以更便捷的进行进程间通信。
同时,由于它一次处理一个请求,因此服务端我们不用考虑线程同步的问题,这是因为服务端此时不存在并发执行的情形。
案例只用到了一个应用,首先是一个 Activity,把Activity 里的代码当作客户端的代码,后面统称客户端;服务端我们用一个 Service 组件(后面统称服务端)来实现,然后通过客户端和 Service 绑定来启动服务端,进而模拟实现客户端和服务端的通信。
同学们这时候有疑问了?
你这不是在同一个进程吗?不急往下看。
这里我们创建好 Service 后,会另外在清单配置文件中给 Service 组件添加一个 process 属性,用来使 Service 组件运行在另一个进程。这样就可以在一个应用中模拟两个进程的运行了,也方便demo的实现。
至于组件的 process 属性,同学们自行查阅资料学习,这里不细说了。
清单配置文件如下图示:
新建一个项目新建操作,我就不给出了,同学们自己完成。
创建 Service项目新建完成后,带有一个 Activity ,我们就当作客户端用,然后再创建一个 Service,命名为 MessengerService.java ,如下图示:
这里只用到了 3 个类,其中 MyConstant.java 的代码如下图示:
别忘记在清单配置文件中给 MessengerService 这个服务配置 process 属性,如下图示:
到这里我们就拥有了客户端和服务端了。
Service 代码说明首先给出代码,如下:
package com.example.messengerdemo.service;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import com.example.messengerdemo.constant.MyConstant;
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
public MessengerService() {
}
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MyConstant.MSG_FROM_CLIENT:
// 接收客户端发来的消息并打印
Log.e(TAG, "receive msg from client:" + msg.getData().getString(MyConstant.MSG));
// 获取客户端的 Messenger 对象,用来回复客户端消息
Messenger clientMessenger = msg.replyTo;
Message replyMessage = Message.obtain(null, MyConstant.MSG_FROM_SERVER);
Bundle bundle = new Bundle();
bundle.putString(MyConstant.REPLY, "消息已经收到,稍后回复你。");
replyMessage.setData(bundle);
try {
clientMessenger.send(replyMessage);// 回复客户端:服务端我已经接收消息
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
// 创建 Messenger 对象,作用是将客户端的发送的消息传递给 MessengerHandler 处理
private final Messenger messenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
// 服务端返回 Messenger 对象底层的 binder
return messenger.getBinder();
}
}
首先,我们创建一个服务端的 Handler,这里命名为 MessengerHandler 继承 Handler,然后重写 handleMessage() 方法来处理客户端发来的消息,如下图示:
那么消息怎么传递给 MessengerHandler 呢,我们就要用到 Messenger 对象了,所以需要创建一个 Messenger 对象,如下图示:
然后你在客户端需要获取到服务端的 Messenger 对象,这样才能给服务端发消息,所以还需要在 onBind() 方法中返回服务端 Messenger 对象底层的 binder,如下图示:
所以我们到这里就可以知道,创建一个 Messenger 对象,他的构造方法里的参数既可以是 Handler 类型参数,也可以是 IBinder 类型的参数,这里通过看类的源码的构造方法可以看出,如下图示:
客户端代码说明首先给出 activity 中的代码,如下:
package com.example.messengerdemo.activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import com.example.messengerdemo.R;
import com.example.messengerdemo.constant.MyConstant;
import com.example.messengerdemo.service.MessengerService;
public class MainActivity extends AppCompatActivity{
// 服务端通过获取客户端的 Messenger 对象 回复消息给客户端 所以客户端也需要声明这么一个 Messenger 对象
private Messenger getReplyMessenger = new Messenger(new MessengerHandle());
private static class MessengerHandle extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MyConstant.MSG_FROM_SERVER:
Log.e("TAG", "receive msg from server:" +
msg.getData().getString(MyConstant.REPLY));
break;
}
}
}
private Messenger messenger = null;// 声明 Messenger 对象,初始化获取服务端的Messenger对象用
// 创建 ServiceConnection 对象
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
messenger = new Messenger(iBinder);// 通过服务端返回的 binder 创建 Messenger 对象
// 创建Message对象并设置携带的数据
Message message = Message.obtain(null, MyConstant.MSG_FROM_CLIENT);
Bundle bundle = new Bundle();
bundle.putString(MyConstant.MSG, "imxiaoqi, enjoy coding.");
message.setData(bundle);
message.replyTo = getReplyMessenger;// 保证服务端处理消息的时候,能获取到客户端的Messenger对象
try {
messenger.send(message);// 通过服务端的Messenger对象发送消息给服务端
} catch (RemoteException e) {
e.printStackTrace();
}
}
// 在正常情况下该方法是不被调用的,它的调用时机是当Service服务被异外销毁时,权例如内存的资源不足时
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e("TAG", "MainActivity onServiceDisconnected callback");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 绑定服务,开启了新的服务进程(后面统一称服务端)
Intent intent = new Intent(MainActivity.this, MessengerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 活动销毁,记得解绑服务
unbindService(connection);
}
}
在onCreate() 方法中,我们绑定服务开启服务端进程,如下图示:
onDestory() 方法就是正常的解绑服务的操作,没什么好说的;
这里同学们应该有注意到,绑定服务的代码中有一个 connection 参数,这个参数其实一个 ServiceConnection 对象,所以我们还需要创建一个 ServiceConnection 对象,这个对象的作用是用来获取服务端的 Messenger 对象并给服务端发送消息,这些操作是在 onServiceConnected() 方法中实现,正常通过绑定方式启动服务并建立连接后,会回调该方法,如下图示:
上图中创建 Message 对象并设置携带的数据以及通过服务端的 Messenger 对象发送消息给服务端大家都比较好理解。
但是有一行代码可能同学们会存在疑惑,就是
message.replyTo = getReplyMessenger;// 保证服务端处理消息的时候,能获取到客户端的Messenger对象
。其实这行代码的作用是:为了在发给服务端的消息中携带客户端的 Messenger 对象,这样服务端就可以通过 message(客户端发来的) 获取客户端的 Messenger 对象,也就能使用这个对象回复消息给客户端说我已经接收到你的消息了。
所以,如果服务端需要回复消息给客户端,那么我们的客户端肯定也是需要有一个 Messenger 对象和 一个 Handler 对象的,只有存在这两个对象才可以接收到服务端的消息并作处理,如下图示:
服务端获取客户端的 Messenger 对象代码,如下图示:
小结到这里代码就全部完成,我们来梳理一下上面都做了什么操作,可以实现什么功能?
我们在一个应用中模拟了客户端和服务端,并运行在不同进程;然后客户端可以发消息给服务端,服务端接收到消息并处理消息,log打印客户端消息;同时服务端收到消息后,再回应客户端说我已经接收到你发给我的消息了,这时候我们在客户端接收到消息并处理后,也用log打印服务端回复的消息。
验证结果我这里连接到了自己的手机,运行应用测试,我们来看一下。
usb连接手机,点击按钮运行应用:
运行成功后打开 logcat 窗口查看日志:
上图说明服务端已经接收到客户端的消息并做了打印
com.example.messengerdemo
这个对应 activity 的进程,com.example.messengerdemo:remote
这个对应 service 组件的进程
切换到 activity 进程打印窗口查看,如下:
上面说明客户端已经接收到了服务端回复的消息
这里再次强调,之所以选择在同一个应用内进行进程间通信,是因为操作起来方便,方便,方便。但是效果和在两个应用间进行进程间通信时一样的。
还有一点大家需要知道:同一个应用的不同组件,如果它们运行在不同进程中,那么和它们分别属于两个应用没有本质区别。
作者:邹奇