深入了解 Messenger 的实现细节

近一个半月因为工作变动的缘故,忙着交接工作和复习面试。没有多少时间来写博客,连一周三次的健身都有几个星期没练了,好多同事问我是胖了还是壮了(我迅速就岔开话题了,机智boy)。上周离职,这周主要在处理一些私事、做些入职准备工作、看点书之类的,下周入职YY(上周才知道原来大神罗升阳也在YY)。好啦,说了这么多,要开始进入 Messenger 的正题了。

前言

看这篇文章前,需要对 Android 的进程间通讯方式有所了解,不然可能会云里雾里。

从使用 Messenger 说起

Android 上实现 IPC (进程间通讯)的方式有好几种,其中有一种就是使用 AIDL 方式实现,对使用 AIDL 不了解的童鞋可以看下方的官方文档(需要梯子)。

https://developer.android.com/guide/components/aidl.html

对于使用 AIDL 方式通讯,其关键就在于创建 aidl 文件,系统会自动为 aidl 文件生成相应的 Java 类,其关键实现在于生成的 Java 类中。

系统提供了一个更方便我们进行 IPC 的类 —— Messenger,先来看看如何使用 Messenger(熟悉的童鞋完全可以跳过这一部分)。

  • 第一步:客户端进程创建两个 Messenger,一个 Sender ,一个 Receiver;

    //客户端进程发消息给服务进程
    private Messenger mSender;
    //客户端进程接收服务进程回调
    private Messenger mReceiver = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Bundle data = msg.getData();
            if (data != null) {
                String response = data.getString("body");
                Toast.makeText(MainActivity.this, response, Toast.LENGTH_SHORT).show();
            }
        }
    });
  • 第二步:编写Service类,并需要在 AndroidManifest.xml 配置多进程;

public class IPCService extends Service {

    private Messenger messenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            try {
                Thread.sleep(2 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message response = Message.obtain();
            Bundle data = new Bundle();
            data.putString("body", "response");
            response.setData(data);
            try {
                msg.replyTo.send(response);//回调客户端
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    });

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();//将Binder返回给请求绑定的进程
    }

}
  • 第三步:绑定Service进程,并在 ServiceConnection 中初始化 Sender;

    Intent intent = new Intent(this, IPCService.class);
    startService(intent);
    bindService(intent, conn, Context.BIND_AUTO_CREATE);//启动绑定Service进程

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mSender = new Messenger(service); //用Binder初始化Sender
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
  • 第四步:用 Sender 和 Receiver 完成客户端进程和服务进程的交互。

    if (mSender != null) {
        Message message = Message.obtain();
        message.replyTo = mReceiver;//将 Receiver 一并发送给服务进程
        try {
             mSender.send(message);
        } catch (RemoteException e) {
             e.printStackTrace();
        }
     }

至此,在绑定服务进程初始化 Sender 后,即可以做多进程间的交互工作了。使用 Messenger 来实现多进程的交互相比我们用 aidl 来要方便得多,但是 Messenger 的内部也是采用 aidl 实现的,只不过为了方便开发者调用而进行一些封装,使得开发者们可以忽略 aidl 的实现细节。简单的了解了 Messenger 的基本使用后,下面我们就来看看 Messenger 的源代码,了解一些内部的实现细节。

Messenger 源代码通读

Messenger 类位于 android.os 包下,代码量不是很多,所以看起来难度不大,只有如下这么几个方法。

在上面的示例代码中可以看到,客户端进程有 Sender 和 Receiver 两个 Messenger,如果不需要实现服务进程回调客户端进程,那么 Receiver 完全可以不要。当需要服务进程回调客户端进程时,则需要传入 Receiver 了。由于需要在进程间传递 Messenger 对象,那么 Messenger 类就必须要继承 Serializable 或者 Parcelable 接口。按照 Android 系统一向的风格,都是偏向于推荐继承 Parcelable 来实现。

所以,上面看到的 describeContents、writeToParcel 方法和 CREATOR 对象实际上继承 Parcelable 的实现。不明白的童鞋可以参照 Parcelable 的官方文档(需要梯子)

https://developer.android.com/reference/android/os/Parcelable.html

而 equals 和 hashCode 方法自然不用多说啦,大家熟悉得很。而 writeMessengerOrNullToParcel 和 readMessengerOrNullFromParcel 这对静态方法主要是实现 Messenger 在 Parcel 中的读写操作的,实现比较简单,大家参见下面代码就可以理解了。

当然,Messenger 除了继承 Parcelable 外,还需要声明一个同名的 Messenger.aidl 文件,可以在 framework 层源码下 android.os 包中找到 Messenger.aidl 文件,对于写过 aidl 的童鞋,肯定不陌生了。

排除掉上面提到的方法,剩下的主要是 Messenger 的两个构造方法以及 getBinder 和 send 方法。客户端调用服务进程方法时,是通过 Messenger 中的 send 方法,所以我们先直接看 send 方法中的内部实现

其内部是调用了 mTarget 的 send 方法,那么 mTarget 又是何物呢?

从上面可以看到 mTarget 是一个 IMessenger 实例,作为 Messenger 唯一的成员变量。 初始化 mTarget 是在 public Messenger(Handler target) 构造函数中,利用 Handler 的 getIMessenger 方法来获取一个 IMessenger 的实例。

从上图可以看到 getIMessenger 前面是没有修饰符的,这样控制了该方法的作用域仅限于 android.os 包内给其他类使用,我们日常开发是无法使用该方法的,所以在 API 文档中也没有提供相应接口文档。在倒数第二行中可以看到 new MessengerImpl() 并在最后 return 给调用者。所以实际上, mTarget 具体实现是在 MessengerImpl 中。

MessengerImpl 实际上是 Handler 的一个私有内部类,它继承了 IMessenger.Stub 并实现 send 方法。用过 aidl 的童鞋 IMessenger.Stub 的身影想必就明白了,实际上 IMessenger 就是系统提供的 IMessenger.aidl 文件,而 IMessenger.Stub 就是由 IMessenger.aidl 生成的类。 IMessenger.aidl 在 framework 层代码的 android.os 包中可以找到,而关于 IMessenger 的 Java 实现,则可以看下面的链接。

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/os/IMessenger.java/

MessengerImpl 的 send 实现相对也比较简单,只有两行代码


    public void send(Message msg) {
        msg.sendingUid = Binder.getCallingUid();
        Handler.this.sendMessage(msg);
    }

首先是将发起调用的客户端进程的 Linux Uid 存储在我们传入的 Message 对象中,服务进程收到 Message 可以通过 msg.sendingUid 得知发起调用的进程的 Linux Uid。接着通过 Handler 的 sendMessage 方法发送给服务进程,这意味着 Messenger 与服务进程间的操作是串行的,因此,在有并行需求的场景下 Messenger 就不适用了。

了解完了 send 方法后,最后就只剩下 getBinder 了,其内部实现也简单。

参照前面的示例代码,这里主要还是在 Service 的 onBind 方法中返回 Binder 对象给客户端调用,实现同样是 IMessenger 的 Java 实现中。

至此,基本上看完了整个 Messenger 的内部代码,从上面的分析上看,内部实现确实非常简单,基于 aidl 的基础上做的封装实现,又对开发者屏蔽了底层 aidl 的实现细节。当然个人认为有两点不足之处:

  • 一个前面提到的 send 操作串行,并行场景则无法用 Messenger;
  • Messenger 内部没有做 Binder 键断裂重连的处理(个人认为内部处理了会更好,更加屏蔽底层的实现细节);

远程调用的阻塞与非阻塞

Android 系统跨进程通讯的底层实现都是通过 Binder 实现,正常情况下客户端进程发起一个远程方法调用的流程大致如下:

1. 客户端线程发起调用;
2. 客户端线程将远程调用操作交给 Binder 线程池,并阻塞等待返回远程方法执行完毕返回;

这也就意味着,我们不能在客户端进程的 UI 线程中发起远程方法调用,不然如果远程方法执行了耗时操作,客户端的 UI 线程将会被阻塞,从而造成 ANR 的问题存在。读者可以自行尝试自定义 aidl 并发起一个耗时的远程方法调用进行验证。但是,如果你使用系统提供的 Messenger ,则不会出现这样的问题,无论你的远程方法执行多么耗时,客户端 Messenger 发起调用后会继续执行接下来的代码,并不会进行阻塞等待。这里让我百思不得其解,为什么呢?前面我们可以看到 Messenger 的 send 方法实现是在 MessengerImpl 中

并且,发送 Messeage 的操作是利用主线程的 Handler ,并没有其他的异步操作,为何执行的过程中不阻塞?这点我也完全没有想明白,最后折腾半天无果向任玉刚老师求助才得到答案。原来,默认情况下发起的远程方法调用都是阻塞式的,但也可以是非阻塞式的。 Messenger 就是采用非阻塞的方式通讯,其关键就在于 IMessenger.aidl 的实现

相比平常自定义的 aidl,多了 oneway 的关键字,声明和不声明 oneway 关键字的在于生成 Java 类中一个参数

不声明 oneway 时,mRemote.transact 传入的最后一个参数是 0;声明 oneway 时,mRemote.transact 传入的最后一个参数是 android.os.IBinder.FLAG_ONEWAY 。

查看 API 文档即可以看到 FLAG_ONEWAY 的作用就是让客户端能够非阻塞的调用远程方法,至此真相大白,如果我们自定义的 aidl 也想实现非阻塞的调用,只需声明 oneway 关键字即可。

总结

因为平时并不常用到 oneway,加上文档提及的很少,唯一有描述的就是下面这段。这次看了 Messenger 的代码才知道有这么回事,也是涨姿势了。

对我的博文感兴趣,可以关注我的简书哟:

http://www.jianshu.com/users/ec95b5891948/latest_articles

Flask+ngrok实现Facebook Messenger机器人公网部署 Webhook是连接聊天机器人与外部消息平台的核心通信机制,其本质是服务端暴露的HTTPS回调端点,遵循标准HTTP协议与签名验证原理。技术价值在于以轻量级方式实现双向实时消息路由,规避复杂网关与中间件。典型应用场景包括客服自动化、营销触达和SaaS工具集成。本文聚焦于使用Flask构建可验证Webhook服务,并借助ngrok快速获取可信HTTPS隧道,完成Facebook Messenger平台接入——涵盖Token验证、消息收发、签名校验及常见故障排查,是Python开发者落地Bot连接层的关键实践。 阅读详情

相关推荐

一个即时通信 Messenger C#代码

日本写的一个即时通信 Messenger C#代码

Android轻量级进程间通信Messenger源码分析

Android中比较有代表性的两大通信机制:1. 线程间Handler通信 2. 进程间Binder通信,本篇文章中我们在理解AIDL原理的基础上来解读一下Messenger的源代码, 并结合示例Demo加深理解。在看本篇文章前,建议先查阅一下笔者的Android 进程间通信机制(六) 手写AIDL文件首先说下我对Messenger的个人理解:1. 从概念上阐述Messenger进程间通信的信使,是一个轻量级的IPC通信方案, 和Message消息不是一个概念。2. 从实现上描述。

u012514113的专栏 1870

RK3568—基于GM8775C的MIPI转双通道LVDS屏幕调试

Rockchip RK3568 原生显示接口不支持双通道LVDS屏幕的数据输出,因此需要借助显示转换芯片才能实现双通道LVDS屏幕的驱动。本文介绍使用GM8775C芯片方案,在 RK3568 平台实现单路 MIPI DSI 信号输出来驱动双通道LVDS显示屏。 关于GM8775C GM8775C 型 DSI 转双通道 LVDS 发送器产品主要实现将 MIPI DSI 转单/双通道 LVDS功能,MIPI 支持 1/2/3/4 通道可选,每通道最高支持 1Gbps 速率,最大支持 4Gbps 速率。LV.

Industio_优快云的博客 1万+

Android进程间通信(一):Messenger

Messeger适用于跨进程通信时不需多线程交互的情况。如若需要跨进程多线程交互,可以使用AIDL(请看《Android进程间通信(二):AIDL》)。一、可运行代码类(Runnable)public interface Runnable { /** * 把需要运行的代码放在这个方法中 */ public void run(); }二、线程类(Thread类)p

Wesnoth的专栏 870

Redis+Unity 数据库搭建

游戏中需要存放排行榜等数据,而且是实时存放,所以就涉及到数据库的问题。这里找服务器大神了解到可以用Redis来做存储,免费的效率极高。

chaixinke的专栏 879

Xilinx DMA IP核心配置与实战解析

本文介绍了Xilinx DMA IP核在直接寄存器模式下的配置与工作流程。DMA通过AXI4-Lite接口接收指令,实现内存与数据流之间的高速传输,无需CPU介入。文章详细说明了开发环境配置、IP参数设置(包括地址位宽、数据宽度等关键参数)以及寄存器操作流程。通过仿真分析,展示了读操作中AXI4接口的突发传输机制,包括地址、长度和突发大小的配置。文中还提供了寄存器地址映射表、信号定义等关键信息,为DMA IP核的应用开发提供了实用参考。

weixin_37688766的博客 884

Android 基于Message的进程间通信 Messenger完全解析

转载请标明出处: http://blog.youkuaiyun.com/lmj623565791/article/details/47017485; 本文出自:【张鸿洋的博客】 一、概述 说到Android进程间通信,大家肯定能想到的是编写aidl文件,然后通过aapt生成的类方便的完成服务端,以及客户端代码的编写。如果你对这个过程不熟悉,可以查看Android a...

weixin_30492047的博客 765

【AI平台】n8n入门2:第一个工作流,调用本地大模型

n8n是一款开源的,专注于AI工作流构建,支持灵活的自定义与集成。就是可以把大模型和其他工具联合起来,这就厉害了。而且,免费功能又强大,目前很火,来研究一下。

正经教主是个热衷于正经学习分享的教主。 4531

Android 基于Message的进程间通信

实例在最后,先简析原理。 1、服务端重要代码解析 @Nullable @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } getBinder里面是这样的 public IBinder getBinder() { return mTarget.asBinder(...

Jeffray1991的博客 365

Chroma向量数据库实战:用Python快速搭建本地知识库(附中文诗词检索案例)

本文详细介绍了如何使用Chroma向量数据库和Python快速搭建本地知识库,并附有中文诗词检索的实战案例。通过解析核心概念、数据准备、向量化及智能检索实现,展示了Chroma在处理中文语义搜索时的轻量级与易用性,为开发者构建AI应用原型提供了实用指南。

weixin_29221753的博客 480

芒果YOLO11改进81:主干Backbone篇之DCNv3结构:即插即用|使用pytorch代码实现,并针对YOLO11专门优化模块

芒果YOLO11改进81:主干Backbone篇之DCNv3结构:即插即用|使用pytorch代码实现,并针对YOLO11专门优化模块

包括YOLOv5、YOLOv7、YOLOv8等模型改进 736

Android -- Wifi的save()操作

Android -- Wifi的save()操作 当我们在Settings中设置静态IP连接时,会调用到WifiManager::save()函数: /** * Save the given network in the supplicant config. If the network already * exists, the configuration is

To be or not to be, it's a question 4323

2026最新华为OD机试新系统机考真题- 真题题库目录|全覆盖题库 + 逐点算法考点详解

2026年4月1日起,华为OD机试正式切换至新系统模式!采用力扣模式,无需自己写输入! 难度大幅度降低。正在备考华为OD机试的同学,优先刷新系统机试真题,可大幅提升通过率。 新系统考题考点与旧版一致,无需重新适应。本专栏所有算法题均配有详细思路解析,并提供C语言、JS、C++、Python、Go、Java多语言源码实现。订阅后私信即可开通专属OJ刷题权限,高效备考一次上岸!

算法大师! 2607

Messenger 消息工具菜单:回复、转发、删除 3 大常用操作的实现与 UI 细节

本文带你拆解 iOS 实时消息应用 Messenger(mChat)中的消息工具菜单:长按任意一条消息,即可唤出 **回复(Reply)、转发(Forward)、删除(Delete)** 三大常用操作。我们将围绕这套消息菜单的交互触发、UI 细节与底层实现,帮助新手理解一个即时聊天 App 是如何优雅地完成消息管理的。 ## 一键唤出:长按消息工具菜单的交互设计 在 Messenger 的聊天

gitblog_01024的博客 741

1.2、基于卷积神经网络(CNN)的车牌自动识别系统(matlab)

基于卷积神经网络(CNN)的车牌自动识别系统(matlab)

XU157303764的博客 2911

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

如何用Symfony Messenger实现异步任务处理?一线架构师实战分享

掌握异步任务处理核心技巧,本文基于PHP Symfony开发指南,详解如何用Symfony Messenger实现消息队列、延迟任务与系统解耦。涵盖常见应用场景、配置步骤及性能优化策略,提升应用响应效率,一线架构师实战经验值得收藏。

InstrFun的博客 1037

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Messenger

Messenger Messenger是一种轻量级的IPC方案,底层实现是AIDL。它对AIDL进行了封装,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,因为不存在并发执行的情形。 服务端进程:需要创建一个Service处理服务端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。 客户端进程:首先需要绑定服务端的Service,绑定成功后用服务端返回的I

时代我西的博客 345

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

详解 C# 中基于发布-订阅模式的 Messenger 消息传递机制:Messenger.Default.Send/Register

文章摘要: 本文详细解析了 C# 中基于发布-订阅模式的 Messenger 消息传递机制,重点介绍了 Messenger.Default.Send 和 Messenger.Default.Register 的使用方法及其在 MVVM 架构中的应用。Messenger 通过松耦合的方式实现跨组件通信,支持消息的发送与订阅,并通过令牌机制实现定向消息传递。文章结合历史对话中的代码示例,展示了如何通过 Messenger 发送和接收消息.

编程技术探索者,分享C/C++、C#、Java、数据库等开发经验,聚焦实战技巧与AI兴趣,助力编程爱好者成长。 2455

探索未来交互:基于Go语言的Facebook Messenger机器人开发库 —— Messenger

探索未来交互:基于Go语言的Facebook Messenger机器人开发库 —— Messenger 在当前这个数字化时代,人机交互正在以前所未有的速度进化。Facebook Messenger作为全球最受欢迎的消息平台之一,提供了无限的可能性给开发者,以创新的方式与用户连接。今天,我们将深入探讨一款名为 Messenger 的开源项目,一个专为构建Facebook Messenger机器人而设...

gitblog_00583的博客 804

MVVM中的Messenger

在开发Wpf/SL应用时,经常会遇到不同页面和窗体之间的参数传递的问题。对于这类问题,我们一般通过事件实现数据传递,也可以定义全局静态变量来进行数据共享。这里我们则使用了另外一种非常高效而优雅的方法来进行消息传递,这里我称之为Messenger,事实上,Messenger并非mvvm的专利,我们可以把它看作一种设计模式,你可以在其它.net程序中使用它。   一、Mvvm Light Mess

yulongguiziyao的专栏 4813

Python部署Facebook Messenger Bot生产实践

Messenger Bot本质是事件驱动的反向代理服务,需满足低延迟响应、高并发连接与无状态伸缩三大工程要求。其核心原理在于Webhook回调机制与Facebook Graph API的严格时序约束,技术价值体现在7×24稳定承载真实业务流量、毫秒级故障定位与跨行业可复用架构。典型应用场景包括电商订单查询、私教预约调度与AI口语陪练等对话式服务。本文聚焦Flask+Gevent轻量协程方案,深入解析签名验证、消息分发三级架构、ECS容器化部署及request_id全链路日志追踪等硬核落地细节,直击Deplo

392

Zenstruck Messenger Monitor Bundle 安装与配置指南

Zenstruck Messenger Monitor Bundle 安装与配置指南 1. 项目目录结构及介绍 Zenstruck's Messenger Monitor Bundle 是一个用于监控Symfony应用中Messenger组件的扩展,它提供了直观的界面来追踪消息工作者、传输、计划任务和消息状态。以下是该仓库的基本目录结构概览及其重要组成部分: .gitattributes, ....

gitblog_00597的博客 762

kickstart.nvim 完整上手指南:从零搭建个人 Neovim 配置的启动点

kickstart.nvim 是一份面向个人 Neovim 配置的"启动点"(launch point)项目:它刻意保持小巧、以单个 `init.lua` 文件为核心、并且逐行注释完整,目标是让你能读懂配置的每一行、按需修改,进而搭建出属于自己的编辑器。本指南将以仓库 [README.md](https://link.gitcode.com/i/57967cd59b6836654e8a725d3b

gitblog_00516的博客 891

【Android】跨进程通信AIDL和messenger详解

1.定义 我们都知道,Android应用一旦启动就会生成一个以包名为名字的进程,当然我们还知道android中很多manager都是运行在system server进程中的,像AMS,PMS,WMS等,它们都是通过binder来进程远程调用,说到这就不得不说多进程之间的通信问题,进程不像线程那样,多线程可以共享内存,而每个进程的内存都是独立的,无法直接访问,因此安卓提供了binder来进行进程间的...

风吹起如花般细碎流年 751

开源项目推荐:基于JavaScript的Wat Talk Messenger Bot

开源项目推荐:基于JavaScript的Wat Talk Messenger Bot 1. 项目基础介绍 本项目是基于Gary Bernhardt的Wat Talk的一个开源项目,它是一个利用JavaScript编写的messenger机器人。该项目可以在GitHub上找到,项目名称为bot-wat。此项目遵循MIT开源协议,允许用户自由使用、修改和分享。 2. 核心功能 该项目的核心功能是作为一...

gitblog_01103的博客 441
上一篇: ScratchView:一步步打造万能的 Android 刮奖效果控件
下一篇: Android应用瘦身,从18MB到12.5MB
D_clock爱吃葱花
博客等级 码龄14年 163粉丝 13原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值