跳至主要內容

Android 中的 Messenger 源码详解

JI,XIAOYONG...大约 3 分钟

前言

Messenger 是 Android 中用于 IPC 的方式之一,使用 Handler 发送有序消息队列,底层是通过 AIDL 调用 Binder 实现。

Messenger 只用于服务端和客户端串行的传递消息,如果大量并发或者跨进程调用服务端的方法,就需要考虑 AIDL 而非 Messenger。

Messenger 的使用可以参考这篇文章open in new window,本文主要探索一下 Messenger 源码实现。

主要使用到的文件:

IMessenger.aidlopen in new window

Messenger.javaopen in new window

Handler.javaopen in new window

解析

一个典型的 Messenger 服务如下所示:

class MessengerService : Service() {

    private val messenger = Messenger(MessengerHandler())

    override fun onBind(intent: Intent?): IBinder? {
        return messenger.binder
    }

    //可以从客户端的得到的 Messenger 中取出该 Handler,并实现客户端->服务端通信
    class MessengerHandler : Handler() {
        override fun handleMessage(msg: Message?) {
            super.handleMessage(msg)
            //客户端的 Messenger,用于服务端->客户端通信,可选
            val client = msg?.replyTo
            client?.send(Message.obtain(null, 2, 1, 2))
        }
    }
}

我们可以看到使用 Handler 创建一个 Messenger,进入到源码看一下:

private final IMessenger mTarget;
public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

我们看到,在这里创建了一个新的与给定的 Handler 绑定在一起的 Messenger,再看看getIMessenger()方法:

final IMessenger getIMessenger() {
    synchronized (mQueue) {
        if (mMessenger != null) {
            return mMessenger;
        }
        mMessenger = new MessengerImpl();
        return mMessenger;
    }
}
private final class MessengerImpl extends IMessenger.Stub {
    public void send(Message msg) {
        msg.sendingUid = Binder.getCallingUid();
        Handler.this.sendMessage(msg);//使用 Handler 发送消息
    }
}

这里我们可以看到getIMessenger()方法会创建一个 MessengerImpl 对象,而这个对象

实现了send()方法,也证实了我们之前的一个观点——Messenger 底层是使用 Handler 发送消息。

同时,看到 MessengerImpl 继承的 IMessenger.Stub 类我们可以联想到这里应该有一个 AIDL 实现:

// /frameworks/base/core/java/android/os/IMessenger.aidl
package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}

oneway 关键字用于修改远程调用的行为。使用该关键字时,远程调用不会阻塞;它只是发送事务数据并立即返回。接口的实现最终接收此调用时,是以正常远程调用形式将其作为来自 Binder 线程池的常规调用进行接收。如果 oneway 用于本地调用,则不会有任何影响,调用仍是同步调用

https://developer.android.google.cn/guide/components/aidl?hl=zh-cnopen in new window

这也就解释了在服务的onBind(intent: Intent?)方法中,我们可以直接使用messenger.binder获取到 Binder 对象的原因。

再看看 Messenger 客户端的实现:

private lateinit var messenger: Messenger//服务端的 Messenger
private val replyMessenger: Messenger = Messenger(ReplyHandler())//客户端的 Messenger,用于服务端->客户端通信,可选
private val mServiceConnection = object : ServiceConnection {
    override fun onServiceDisconnected(name: ComponentName?) {

    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        messenger = Messenger(service)//注意这里的构造方法传入的是 IBinder 对象
        val message = Message.obtain(null, 1)
        message.replyTo = replyMessenger
        val data = Bundle()
        data.putString("msg", "Hello World")
        try {
            messenger.send(message)//使用服务端的 Messenger 向服务端发送消息
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

}

可以注意到在客户端通过Messenger(IBinder target)取得服务端的 Messenger,而这里的 IBinder 对象则是通过服务端的 Messenger 的getBinder()获取的:

public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}

Stub.asInterface()方法我们在之前的文章open in new window中介绍过,他会根据客户端和服务端是否在同一进程而决定返回 Stub 实例还是 Proxy 类实例以实现跨进程通信。

而通过比较 Messenger(IBinder target)Messenger(Handler target)两个构造方法我们也可以知道,两个方法都只是用来初始化了IMessenger mTarget对象,这也就解释了在服务端和客户端可以通过两个不同的构造方法获取到有同样功能的 Messenger。

参考资料

《Android 开发艺术探索》

Android 接口定义语言 (AIDL)open in new window

文章标题:《Android 中的 Messenger 源码详解》
本文作者: JI,XIAOYONG
发布时间: 2019/03/21 10:26:32 UTC+8
更新时间: 2023/12/30 16:17:02 UTC+8
written by human, not by AI
本文地址: https://jixiaoyong.github.io/blog/posts/b0dc2559.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 许可协议。转载请注明出处!
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8