如何使用 Messenger 实现单向和双向通信?

解读

国内面试中,Messenger 不是“会不会用”的问题,而是“为什么用它、怎么用才安全、怎么证明你懂 Binder”的综合性考点。
面试官常把 Messenger 与 AIDL、广播、ContentProvider、Socket 并列,考察你对“轻量级 IPC”选型边界的理解;同时通过“单向/双向”追问,验证你对 Binder 单向序列化、Handler 线程模型、内存泄漏、Crash 隔离的掌握程度。
回答时必须给出可落地的模板代码,并主动说出“服务端回发时 Client 的 Messenger 从哪来”“如何避免 Client 端 Handler 内存泄漏”“服务端 crash 后如何快速感知”这三点,才能拿到高分。

知识点

  1. Messenger 本质是对 Binder 的二次包装,底层仍走 /dev/binder,数据载体是 Message,天然线程安全。
  2. 单向通信:Client 持有 Server 的 Messenger,只发不收;Server 端在 Service.onBind 返回 Messenger.getBinder()。
  3. 双向通信:Client 在 send() 前把自身的 Messenger 塞进 Message.replyTo 字段;Server 端收到后通过 msg.replyTo.send() 回传。
  4. 线程模型:Server 端 Messenger 内部构造的 Handler 运行在主线程,耗时任务需手动切线程;Client 端回传 Handler 建议放在子线程并配合 WeakReference,防止 Service 持引用导致 Activity 泄漏。
  5. 数据限制:Message.obj 只能传实现了 Parcelable 的对象,且大小受 Binder 1M-8K 缓冲区限制;大图必须走共享内存或 ContentProvider openFile。
  6. 稳定性:服务端进程被杀后 Client 会收到 DeadObjectException,需捕获并重连;Client 端可通过 DeathRecipient 监听 BinderDied。
  7. 国内 ROM 特性:华为、小米、OPPO 后台冻结策略下,Service 拉回前台需同时调用 startForeground 并申请“自启动”白名单,否则双向回传会延迟甚至丢失。
  8. 与 AIDL 对比:Messenger 队列化、单线程、无并发,适合低频调用;AIDL 支持并发回调,但需写 .aidl 文件,维护成本高。
  9. 与广播对比:Messenger 点对点、隐私数据不外泄;广播易被系统拦截且 Android 14 后台广播受限。
  10. 安全加固:服务端在 onBind 时校验调用方包名/签名,防止第三方 App 通过隐式 Intent 绑定;敏感指令配合自定义权限 signature|privileged。

答案

  1. 单向通信(Client → Server)
    服务端

    class SingleWayService : Service() {
        private val handler = Handler(Looper.getMainLooper()) { msg ->
            when (msg.what) {
                1 -> Log.d("Server", "收到单向消息:${msg.arg1}")
            }
            true
        }
        private val messenger = Messenger(handler)
        override fun onBind(intent: Intent): IBinder = messenger.binder
    }
    

    客户端

    val intent = Intent().setComponent(
        ComponentName("com.example.server", "com.example.server.SingleWayService")
    )
    bindService(intent, object : ServiceConnection {
        var serverMessenger: Messenger? = null
        override fun onServiceConnected(name: ComponentName, binder: IBinder) {
            serverMessenger = Messenger(binder)
            val msg = Message.obtain(null, 1).apply { arg1 = 10086 }
            serverMessenger?.send(msg)
        }
        override fun onServiceDisconnected(name: ComponentName) {}
    }, BIND_AUTO_CREATE)
    
  2. 双向通信(Client ⇄ Server)
    服务端

    class TwoWayService : Service() {
        private val handler = Handler(Looper.getMainLooper()) { msg ->
            when (msg.what) {
                2 -> {
                    val clientMessenger = msg.replyTo
                    val reply = Message.obtain(null, 3).apply { arg1 = msg.arg1 * 2 }
                    try {
                        clientMessenger?.send(reply)
                    } catch (e: RemoteException) {
                        // Client 进程被杀
                    }
                }
            }
            true
        }
        private val messenger = Messenger(handler)
        override fun onBind(intent: Intent): IBinder = messenger.binder
    }
    

    客户端

    class ClientActivity : AppCompatActivity() {
        private var serverMessenger: Messenger? = null
        private val clientHandler = object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                if (msg.what == 3) {
                    Log.d("Client", "收到服务端回执:${msg.arg1}")
                }
            }
        }
        private val clientMessenger = Messenger(clientHandler)
        private val conn = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName, binder: IBinder) {
                serverMessenger = Messenger(binder)
                val msg = Message.obtain(null, 2).apply {
                    arg1 = 42
                    replyTo = clientMessenger   // 关键字段
                }
                serverMessenger?.send(msg)
            }
            override fun onServiceDisconnected(name: ComponentName) {}
        }
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val intent = Intent().setComponent(
                ComponentName("com.example.server", "com.example.server.TwoWayService")
            )
            bindService(intent, conn, BIND_AUTO_CREATE)
        }
        override fun onDestroy() {
            super.onDestroy()
            unbindService(conn)
            clientHandler.removeCallbacksAndMessages(null) // 防泄漏
        }
    }
    

    要点小结

    • replyTo 必须手动设置,否则服务端无法回传。
    • 服务端回传前捕获 RemoteException,防止 Client 被杀导致 Service Crash。
    • Client 端在 onDestroy 释放 Handler 回调,避免 Service 持引用造成内存泄漏。
    • 国内后台限制场景下,Service 端需在 5 秒内调用 startForeground(),否则双向通信会被系统中断。

拓展思考

  1. 如果业务需要“一对多”广播式回传,Messenger 模型会因 replyTo 只能指向单 Client 而失效,此时应改用 AIDL + RemoteCallbackList 或 BroadcastReceiver + IntentFilter(Android 14 需加 RECEIVER_EXPORTED 标记)。
  2. 对性能要求极高的车载场景,Messenger 的序列化/队列化会成为瓶颈,可改用共享内存(MemoryFile + ParcelFileDescriptor)+ Binder 一次握手传递 fd,实现零拷贝。
  3. 国内隐私合规趋严,若 Messenger 传输 IMEI、定位等敏感信息,需在 Manifest 声明 android:protectionLevel="signature" 的自定义权限,并在服务端 onBind 通过 checkCallingPermission() 校验;否则应用市场审核会被驳回。
  4. 为了灰度容错,可在 Client 端封装 MessengerManager,内部维护“BinderDied + 指数退避重连”机制,当检测到 RemoteException 或 DeathRecipient 回调时,自动释放旧 Messenger 并重新 bindService,保证用户无感知。