2026-02

Messenger IPC 通信全面笔记

从基础概念到高级用法,从单向通信到多客户端管理,从内存泄漏到完整解决方案

Messenger Handler IPC 进程通信
什么是 Messenger?
1 核心概念与组件

Messenger 是 Android 提供的一种轻量级跨进程通信(IPC)方式,基于 Binder 实现。

你可以把它想象成:两个进程之间的快递员。客户端把数据打包成 Message(包裹),交给 Messenger(快递员),它通过 Binder 驱动把消息送给服务端的 Handler(收件人)。

核心组件:

  • Handler:负责接收和处理消息
  • Messenger:封装 Handler,提供跨进程收发消息的能力
  • Message:消息体,数据载体是内部的 Bundle(msg.data)
  • Service:跨进程通信的"基地",通过 onBind 暴露 Messenger 的 IBinder
Messenger 与 Intent 的异同
2 核心区别对比

相同点:最终跨进程传递的数据都是 Bundle,都受 Binder 1MB 限制,都必须实现 Parcelable。

核心区别:
  • Intent:一次性的"信件",用于启动组件,单向传递
  • Messenger:长连接的"对讲机",绑定服务后可随时收发消息,天然支持双向通信
类比:Intent 是寄信,Messenger 是打电话。
基本单向通信
3 客户端 → 服务端通信实现

1. 服务端实现(Service)

class MessengerService : Service() {
    private val handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            val name = msg.data.getString("name")
            // 处理消息...
        }
    }
    private val messenger = Messenger(handler)

    override fun onBind(intent: Intent): IBinder = messenger.binder
}

记得在 AndroidManifest.xml 中声明 Service,如需跨应用可设置 exported="true"

2. 客户端绑定并发送消息

class MainActivity : AppCompatActivity() {
    private var serviceMessenger: Messenger? = null

    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            serviceMessenger = Messenger(service)
            val msg = Message.obtain().apply {
                data = Bundle().apply { putString("name", "张三") }
            }
            serviceMessenger?.send(msg)
        }
        override fun onServiceDisconnected(name: ComponentName?) { serviceMessenger = null }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val intent = Intent(this, MessengerService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }

    override fun onDestroy() {
        super.onDestroy()
        unbindService(connection)
    }
}
双向通信:利用 replyTo
4 客户端与服务端互相收发消息

要让服务端也能给客户端发消息,客户端需要把自己的 Messenger 通过 msg.replyTo告诉服务端。

1. 客户端构建回信地址

private val replyHandler = object : Handler(Looper.getMainLooper()) {
    override fun handleMessage(msg: Message) {
        val reply = msg.data.getString("reply")
        // 显示/处理回复
    }
}
private val clientMessenger = Messenger(replyHandler)

发送消息时设置 replyTo:

val msg = Message.obtain().apply {
    data = Bundle().apply { putString("name", "张三") }
    replyTo = clientMessenger   // 把自己的 Messenger 传过去
}
serviceMessenger?.send(msg)

2. 服务端回复

override fun handleMessage(msg: Message) {
    val replyMessenger = msg.replyTo
    val replyMsg = Message.obtain().apply {
        data = Bundle().apply { putString("reply", "收到") }
    }
    replyMessenger?.send(replyMsg)
}
多客户端注册与消息转发
5 实现客户端之间消息转发

服务端维护一个 Map<String, Messenger>,每个客户端在连接后先"注册",把自己的名字和 Messenger 告诉服务端,之后可实现客户端之间的消息转发。

1. 服务端维护客户端列表

private val clients = mutableMapOf<String, Messenger>()

// 处理注册
MessengerConstant.MSG_REGISTER -> {
    val clientMessenger = msg.replyTo
    val clientName = msg.data.getString("client_name")
    if (clientMessenger != null && clientName != null) {
        clients[clientName] = clientMessenger
        // 回复注册成功
    }
}

// 处理转发
MessengerConstant.MSG_SEND_ANOTHER -> {
    val target = msg.data.getString("target")
    val message = msg.data.getString("msg")
    val messenger = clients[target] ?: return
    messenger.send(Message.obtain().apply {
        data = Bundle().apply { putString("msg", message) }
    })
}

2. 客户端注册

override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    serviceMessenger = Messenger(service)
    val msg = Message.obtain().apply {
        what = MSG_REGISTER
        data = Bundle().apply { putString("client_name", "MainActivity") }
        replyTo = clientMessenger   // 传自己的 Messenger
    }
    serviceMessenger?.send(msg)
}
注意:一定要在 onDestroy 时发送 MSG_UNREGISTER 注销,并处理 Service 端的移除逻辑。
关键问题:强引用链、内存泄漏与消息积压
6 三大核心问题解析

1. 强引用链导致 Activity 无法回收

从 Service 到 Activity 存在一条强引用链:

Service 的 clients Map (强引用)
    → Messenger (内部持有)
    → Handler (匿名内部类隐式持有)
    → Activity 实例
问题:只要 clients 不 remove 掉 Messenger,Activity 即使 onDestroy 也无法被 GC,造成内存泄漏。

2. 进程存活 ≠ Activity 存活

  • Activity 销毁后,其所在的独立进程不一定会立即被杀(取决于系统内存和其他组件)
  • Handler 依附于主线程的 Looper,与进程同生命周期,所以消息仍能投递和处理
  • 但 Toast 等 UI 操作会因 Context 失效而失败

3. 消息积压现象

  • 退出 Activity 后,服务端继续发送消息,这些消息会排队在主线程的消息队列中
  • 当 Activity 重新打开(同一进程)时,这些积压的消息会瞬间全部弹出(Toast 显示)
日志示例:退出 SecondActivity 后点三次发送,重进 SecondActivity 时立刻连续弹出三条 Toast。
解决方案:完整的注册/注销机制
7 三步走策略避免内存泄漏

客户端(Activity)销毁时必须:

  1. 清空自己 Handler 的消息队列(防积压)
  2. 发送注销消息给 Service(切断强引用链)
  3. 解绑服务
override fun onDestroy() {
    // 1. 清空 Handler 所有消息,防止积压
    handler.removeCallbacksAndMessages(null)

    // 2. 通知 Service 移除自己
    messengerService?.send(Message.obtain().apply {
        what = MSG_UNREGISTER
        data = Bundle().apply { putString("client_name", MY_NAME) }
    })

    // 3. 解绑
    if (isBound) {
        unbindService(messengerConnection)
        isBound = false
    }
    super.onDestroy()
}

服务端处理注销

MSG_UNREGISTER -> {
    val name = msg.data.getString("client_name")
    clients.remove(name)
}

服务端发送消息时捕获异常

try {
    messenger.send(msg)
} catch (e: Exception) {
    clients.remove(target)  // 发送失败时清理失效客户端
}
如何验证对象被 GC 回收?
8 使用 WeakReference 监控对象生命周期

使用 弱引用(WeakReference)来观察 Activity 是否被回收。

companion object {
    var weakActivity: WeakReference<SecondActivity>? = null
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    weakActivity = WeakReference(this)
}

// 在需要检查的地方
Log.d("GC_Check", "Activity是否存活? ${SecondActivity.weakActivity?.get() != null}")
  • 如果输出 false,说明 Activity 已被 GC 回收
  • WeakReference 不会阻止对象被 GC 回收,适合用于监控对象生命周期
Messenger 与 AIDL 的对比
9 特性对比表格
特性 Messenger AIDL
接口定义 无需 AIDL 文件,纯 Java/Kotlin 需要编写 .aidl 文件
线程模型 服务端 Handler 串行处理消息 Binder 线程池并发处理
双向通信 通过 replyTo 天然支持 需定义回调接口
多客户端管理 手动维护 Map 可配合 RemoteCallbackList
适用场景 简单消息收发、轻量级推送 多方法、高并发、强类型接口
数据限制 < 1MB(Binder 限制) < 1MB(Binder 限制)

结论

Messenger 足够胜任大多数普通 IPC 场景;当业务复杂、方法众多或需要并发时,才需要用 AIDL。

最佳实践总结
10 六条核心要点

始终成对出现:register 和 unregister 必须配对,避免内存泄漏

销毁时清空消息:handler.removeCallbacksAndMessages(null) 防止积压

服务端容错:send() 时 try-catch,清理失效客户端

使用弱引用或 Application Context避免 UI 上下文泄漏(但彻底注销才是根本)

不同应用通信时:指定 Service 的包名,并注意权限控制

大数据勿用 Messenger:超大文件应使用文件共享或 ContentProvider

通过这些实践

你可以用 Messenger 实现一个稳定、安全、不泄漏的跨进程通信系统。