Android Messenger 笔记

1 阅读3分钟

replyToMessenger 的跨进程通信(IPC, Inter-Process Communication)中,扮演着至关重要的角色——它是建立起客户端与服务端之间双向通信通道的桥梁

你可以把它想象成一个“回信地址”。当客户端向服务端发送消息时,通过在消息中附带上自己的 replyTo 信使,就相当于告诉服务端:“如果你需要回复我,请寄到这个地址。”

下面,我们来详细拆解这个过程。

🧭 replyTo 的核心作用与双向通信流程

Messenger 本身是一种轻量级的 IPC 方案,其底层实现是对 AIDL 的封装,因此使用起来非常简单 。但它的设计是“单工”的,即默认只支持单向通信。要实现双向对话,就必须依赖 replyTo

整个流程可以用以下几个步骤清晰地概括 :

sequenceDiagram
    participant Client
    participant Server

    Note over Client: 1. 创建自己的Handler<br/>和Messenger (clientMessenger)
    Client->>Server: 2. 绑定服务,获得服务端Messenger
    Client->>Server: 3. 创建Message,设置数据<br/>并执行 msg.replyTo = clientMessenger
    Server->>Server: 4. 服务端Handler处理消息
    Server->>Client: 5. 从msg.replyTo中取出clientMessenger<br/>用它向客户端发送回复消息
    Client->>Client: 6. 客户端Handler处理服务端的回复

💻 代码实现详解

下面通过一个简单的示例,来看看代码中是如何体现的。

📱 客户端(Client)

客户端需要做两件事:一是发送消息,二是准备好接收回复的“信箱”。

// 1. 创建自己的“信箱”,用于接收服务端的回复
private static class ReplyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_REPLY_FROM_SERVICE:
                // 收到服务端的回复,进行UI更新等操作
                String replyText = msg.getData().getString("reply");
                Log.i("Client", "收到服务端回复: " + replyText);
                break;
        }
    }
}
// 客户端的信使,专门负责接收回复
private Messenger mReplyMessenger = new Messenger(new ReplyHandler());

// 2. 与服务端建立连接后,发送消息
private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        // 拿到服务端的信使
        Messenger mServiceMessenger = new Messenger(service);
        // 创建要发送的消息
        Message msg = Message.obtain(null, MSG_FROM_CLIENT, 0, 0);
        Bundle data = new Bundle();
        data.putString("request", "你好,服务端!");
        msg.setData(data);
        
        // ***** 关键步骤:设置 replyTo,附上自己的“回信地址” *****
        msg.replyTo = mReplyMessenger;
        
        try {
            mServiceMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    public void onServiceDisconnected(ComponentName className) { }
};

💻 服务端(Server)

服务端的任务则是接收消息、解析 replyTo,并使用它来回复。

public class MessengerService extends Service {
    private static class ServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_FROM_CLIENT:
                    // 1. 处理客户端发来的请求
                    String request = msg.getData().getString("request");
                    Log.i("Server", "收到客户端请求: " + request);
                    
                    // 2. ***** 关键步骤:通过 msg.replyTo 拿到客户端的信使 *****
                    Messenger clientMessenger = msg.replyTo;
                    
                    // 3. 创建回复消息
                    Message replyMsg = Message.obtain(null, MSG_REPLY_FROM_SERVICE);
                    Bundle replyData = new Bundle();
                    replyData.putString("reply", "你的消息已收到,谢谢!");
                    replyMsg.setData(replyData);
                    
                    // 4. 使用客户端的信使将消息发回
                    try {
                        clientMessenger.send(replyMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    }
    
    private final Messenger mServerMessenger = new Messenger(new ServiceHandler());
    
    @Override
    public IBinder onBind(Intent intent) {
        // 返回底层的Binder,供客户端构造自己的Messenger
        return mServerMessenger.getBinder(); 
    }
}

关键点总结

  1. 服务端:通过 msg.replyTo 获取客户端信使,并使用它发送回复 。
  2. 客户端:创建自己的 Messenger 并赋值给 msg.replyTo,以此将自己的“回信地址”传递给服务端 。

💡 进阶要点与注意事项

了解基本用法后,以下几个要点能帮你更深入地理解和使用 Messenger

  • 串行处理Messenger 在服务端是通过 Handler 来处理消息的。这意味着所有来自客户端的请求会被串行地在服务端的 Handler 所在线程中执行,不存在并发问题,因此你无需考虑线程同步,这是它相较于直接使用 AIDL 的一个主要优势 。
  • 与 AIDL 的对比:如果说 AIDL 是让你能够直接调用远程进程的方法,那 Messenger 就是一种基于消息的、更简单的 IPC 方式。你可以把 Messenger 理解为系统对 AIDL 的一次封装,它更适合于消息驱动的跨进程通信场景 。
  • 数据传递限制:通过 Messenger 发送的 Message 对象,其传递的数据大小受 Binder 事务缓冲区限制,通常为 1MB。这是所有 Binder 通信的共同限制,需要注意避免传递过大的数据,否则会引发 TransactionTooLargeException 异常 。

希望这份讲解能帮助你完全掌握 replyToMessenger 跨进程通信中的妙用。如果对其中某个环节还有疑问,比如它的底层实现或与其他IPC方式的对比,我们可以继续探讨。