一句话理解: Messenger 就是给
Handler套了一个跨进程的壳。服务端用Handler创建Messenger,客户端绑定后通过Messenger.send(Message)发消息,服务端再通过msg.replyTo.send(...)把结果回传。最大优点: 不需要写
.aidl文件,也不需要手写 Stub/Proxy 三件套,是 Android 里最轻量的 IPC 方案。
一、适用场景
| 场景 | 是否适合 Messenger |
|---|---|
| 只需要"发消息",不需要"调方法" | ✅ 非常适合 |
| 需要同步等待返回值(阻塞调用) | ❌ 不适合,Messenger 是纯异步的 |
| 数据传输量大、频率高 | ❌ 不适合,Binder 传输有 1MB 限制 |
| 快速实现双向通信 | ✅ 非常适合,自带 replyTo 机制 |
二、服务端实现
服务端的核心思路:创建一个运行在子线程的 Handler,再用它构造 Messenger,最后把 Messenger 的 Binder 暴露给客户端。
MessengerService.java
java
package com.qhsoft.foodsafety.module;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
// 定义消息类型常量,客户端和服务端必须保持一致
public static final int MSG_SAY_HELLO = 1;
public static final int MSG_ADD = 2;
private Messenger mMessenger; // 服务端的 Messenger
private HandlerThread mHandlerThread;
/**
* 内部 Handler:处理客户端发来的消息
* 运行在 HandlerThread 的子线程中,避免阻塞主线程
*/
private class ServiceHandler extends Handler {
ServiceHandler(android.os.Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
// 从 Message 中读取客户端传来的数据
String clientName = msg.getData().getString("name", "client");
Log.d(TAG, "收到客户端问候: " + clientName);
// msg.replyTo 是客户端留给我们的"回信地址"
Messenger replyTo = msg.replyTo;
if (replyTo != null) {
// 构造回复消息
Message reply = Message.obtain(null, MSG_SAY_HELLO);
Bundle bundle = new Bundle();
bundle.putString("reply", "Hello " + clientName + ", I am MessengerService!");
reply.setData(bundle);
try {
replyTo.send(reply); // 向客户端回传结果
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case MSG_ADD:
// 从 Message.arg1 / arg2 中读取整数参数
int a = msg.arg1, b = msg.arg2, sum = a + b;
Log.d(TAG, "收到计算请求: " + a + " + " + b + " = " + sum);
Messenger replyToAdd = msg.replyTo;
if (replyToAdd != null) {
Message reply = Message.obtain(null, MSG_ADD);
reply.arg1 = sum; // 把计算结果放在 arg1 里回传
try {
replyToAdd.send(reply);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
default:
super.handleMessage(msg);
}
}
}
@Override
public void onCreate() {
super.onCreate();
// 创建子线程的 Looper,避免服务端处理消息时卡主线程
mHandlerThread = new HandlerThread("MessengerServiceThread");
mHandlerThread.start();
// 用子线程的 Looper 创建 Handler,再用 Handler 创建 Messenger
mMessenger = new Messenger(new ServiceHandler(mHandlerThread.getLooper()));
}
@Override
public IBinder onBind(Intent intent) {
// 把 Messenger 内部的 Binder 返回给客户端
// 客户端拿到这个 IBinder 就能向服务端发消息了
return mMessenger.getBinder();
}
@Override
public void onDestroy() {
super.onDestroy();
if (mHandlerThread != null) {
mHandlerThread.quitSafely(); // 优雅停止子线程
}
}
}
💡 为什么要用 HandlerThread? 服务端收到客户端消息后需要处理业务逻辑(比如上面的加法运算),如果直接放在主线程 Handler 里,耗时操作会卡死主线程。用
HandlerThread可以把处理逻辑放到子线程。
三、在 AndroidManifest.xml 中注册服务
ini
<service
android:name="com.qhsoft.foodsafety.module.MessengerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.qhsoft.foodsafety.module.MESSENGER_SERVICE" />
</intent-filter>
</service>
⚠️ 注意:
android:exported="true"允许其他应用绑定。如果是内部应用通信,建议加上android:permission限制访问权限。
四、客户端实现
客户端的核心思路:绑定服务端 Service,拿到 IBinder 后构造 Messenger,然后通过 Messenger.send(Message) 发消息。为了接收服务端回复,客户端也需要创建一个 Messenger(通过 Handler),并把它塞进 Message.replyTo 里。
MainActivity.kt
kotlin
class MainActivity : BaseActivity() {
private val TAG by lazy { MainActivity::class.simpleName }
private var messenger: Messenger? = null // 指向服务端的 Messenger
private var messengerBound = false
private val MSG_SAY_HELLO = 1
private val MSG_ADD = 2
/**
* 客户端的 Handler:处理服务端回传的消息
* 运行在主线程,可以安全更新 UI
*/
private val clientHandler = Handler(Looper.getMainLooper()) { msg ->
when (msg.what) {
MSG_SAY_HELLO -> {
val reply = msg.data?.getString("reply")
Log.d(TAG, "【Messenger】收到服务端问候回复: $reply")
}
MSG_ADD -> {
val sum = msg.arg1
Log.d(TAG, "【Messenger】收到计算结果: $sum")
}
}
true
}
/**
* 客户端的 Messenger:给服务端用来"回信"
* 它内部持有一个 Binder,会被序列化传给服务端
*/
private val clientMessenger = Messenger(clientHandler)
private val messengerConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
// service 是服务端 Messenger 的 Binder 代理(跨进程)
// 用它构造客户端的 Messenger 对象
messenger = Messenger(service)
messengerBound = true
Log.d(TAG, "【Messenger】服务连接成功")
// 构造第一条消息:向服务端问好
val msg = Message.obtain(null, MSG_SAY_HELLO)
val bundle = Bundle()
bundle.putString("name", "Sorting")
msg.data = bundle
// ★ 关键:把客户端 Messenger 贴到信封上
// 服务端收到后通过 msg.replyTo 就能向客户端回消息
msg.replyTo = clientMessenger
try {
messenger?.send(msg)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onServiceDisconnected(name: ComponentName) {
messenger = null
messengerBound = false
Log.w(TAG, "【Messenger】服务断开")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val messengerIntent = Intent().apply {
action = "com.qhsoft.foodsafety.module.MESSENGER_SERVICE"
`package` = "com.qhsoft.foodsafety" // 指定服务端应用包名
}
val messengerBindResult = bindService(
messengerIntent,
messengerConnection,
Context.BIND_AUTO_CREATE
)
Log.d(TAG, "【Messenger】bindService result=$messengerBindResult")
}
override fun onDestroy() {
super.onDestroy()
if (messengerBound) {
unbindService(messengerConnection)
messengerBound = false
}
}
}
五、为什么代码里有两个 Messenger?
这是很多新手容易混淆的地方。实际上,两个 Messenger 分别住在客户端 和服务端,各司其职:
scss
┌─────────────────┐ ┌─────────────────┐
│ 客户端 │ │ 服务端 │
│ │ │ │
│ clientMessenger│◄──── replyTo.send ───┤ mMessenger │
│ (回信地址) │ │ (收信邮箱) │
│ ↑ │ │ ↑ │
│ clientHandler │ │ ServiceHandler │
│ (处理回信) │ │ (处理来信) │
└─────────────────┘ └─────────────────┘
| 代码 | 角色 | 作用 |
|---|---|---|
mMessenger(服务端) |
服务端信箱 | 接收客户端发来的消息,交给 ServiceHandler 处理 |
messenger(客户端) |
指向服务端的代理 | 客户端用它调用 send(msg),把消息发到服务端 |
clientMessenger(客户端) |
客户端的回信地址 | 塞进 msg.replyTo,让服务端知道"回消息找谁" |
关键代码拆解:
scss
// 1. 发消息时,把"我家地址"贴到信封上
msg.replyTo = clientMessenger
// 2. 通过服务端的 Messenger 把消息发过去
messenger?.send(msg)
// 3. 服务端收到后,从信封上找到回信地址,把结果回传
replyTo.send(reply)
六、Messenger 跨进程原理
Messenger 本质上是对 Binder 的轻量封装,底层走的仍然是 Android 的 Binder IPC 机制。下面是完整的数据流转过程:
6.1 服务端侧:Messenger = IBinder + Handler
服务端创建 Messenger 时:
ini
mMessenger = new Messenger(new ServiceHandler(looper));
内部做了两件事:
- 创建一个
IMessenger.Stub对象 (继承自Binder),实现asBinder()返回自身 - 保存传入的 Handler :当收到跨进程消息时,把
Message投递到这个Handler的消息队列
onBind() 返回 mMessenger.getBinder(),实际上返回的就是这个 IMessenger.Stub(一个 Binder 对象)。
6.2 客户端侧:Messenger = 指向远程 Binder 的代理
客户端在 onServiceConnected 中:
scss
messenger = Messenger(service) // service 是 IBinder
这里的 service 是 Binder 驱动分配给客户端的远程 Binder 代理 (Proxy)。Messenger 把它包装起来,调用 send(msg) 时,内部会执行:
- 把
Message写入Parcel(序列化what/arg1/arg2/data/replyTo等字段) - 调用
mRemote.transact(Stub.TRANSACTION_send, data, reply, 0) - Binder 驱动 把
Parcel数据拷贝到服务端进程 - 服务端
IMessenger.Stub.onTransact()被调用,反序列化出Message - 将
Message投递到ServiceHandler的Looper队列,最终执行handleMessage()
6.3 replyTo 的双向通信原理
Message 的 replyTo 字段也是一个 Messenger(实现了 Parcelable 接口)。
当客户端设置 msg.replyTo = clientMessenger 时:
clientMessenger内部持有一个IMessenger.Stub(Binder),会被序列化 到Parcel中- 服务端反序列化后,
replyTo变成指向客户端 Messenger 的远程代理 - 服务端调用
replyTo.send(reply)时,走的也是同样的 Binder IPC 流程,只是方向相反
这样就实现了 客户端 → 服务端 → 客户端 的完整双向通信,而两端代码都是基于 Handler 的异步消息模型。
八、写在最后
Messenger 是 Android IPC 的"轻量级选手",它牺牲了一部分灵活性(不能同步返回值、不能随意定义接口),换来了极简的开发体验。如果你的跨进程需求只是"发消息、等回调",Messenger 往往是比 AIDL 更优雅的选择。
📌 一句话记住: Messenger 就是 Handler 的"跨进程版",一端发消息、一端 Handler 处理,双向通信靠
replyTo贴地址。