当前位置: 首页 > news >正文

使用 Messenger 跨进程通信

什么是Messenger

Messenger 也是IPC的方案之一,是基于消息的跨进程通信。基于消息是什么意思?Handler是我们最常用的消息机制,所以 Messenger 对于使用者来说就像是使用 Handler。实际上 Messenger 就是 AIDL 的上层封装而已,它们的底层实现原理都是基于 Binder 的。

Messenger的使用

服务端

import android.app.Service
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.util.Logconst val MSG_CLIENT = 0x110
const val MSG_SERVER = 0x111
class MessengerService: Service() {private val mMessenger = Messenger(object : Handler(Looper.getMainLooper()){override fun handleMessage(msg: Message) {Log.d("MessengerService", "currentThread ->" + Thread.currentThread().name)when(msg.what) {MSG_CLIENT -> {// 除非就是一个简单的整型,可以把值放到 arg 等属性上,否则最好使用 bundleval bundle = msg.dataLog.d("MessengerService", "name=${bundle.get("name")}; age=${bundle.get("age")}; height=${bundle.get("height")}")// 服务端封装msgval replyMsg = Message.obtain()replyMsg.what = MSG_SERVERval bundleToC = Bundle()bundleToC.putString("msg",  "我收到了Client的消息")replyMsg.data = bundleToC// 服务端发送给客户端,这里的replyTo是客户端的messenger实例msg.replyTo.send(replyMsg)}}super.handleMessage(msg)}});override fun onBind(intent: Intent?): IBinder? {return mMessenger.binder}
}

Messenger 本身就是 AIDL 的封装,因此还是通过 serviceonBind() 方法里返回 mMessenger.binder。然后它就像是使用 Handler 一样在 handleMessage() 方法内接收 msg。同样的需要在 AndroidManifest.xml 文件中声明该 service

<service android:name=".MessengerService"android:exported="true"><intent-filter><action android:name="com.example.messengerservice"></action></intent-filter>
</service>

客户端

在客户端第一步还是要绑定 Service

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.os.RemoteException
import android.util.Log
import androidx.activity.ComponentActivityclass MainActivity: ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)bindService()}var mMessenger: Messenger? = nullprivate val mServiceConnection: ServiceConnection = object : ServiceConnection {override fun onServiceConnected(name: ComponentName, service: IBinder) {mMessenger = Messenger(service)Log.i("MainActivity", "onServiceConnected IBinder = $service")}override fun onServiceDisconnected(name: ComponentName) {mMessenger = null}}private fun bindService() {val intent = Intent()intent.component = ComponentName("com.example.interprocess", "com.example.service.MessengerService")bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)}fun send() {mMessenger?.let { messenger ->val msg = Message.obtain()msg.what = MSG_CLIENTval bundle = Bundle().apply {putString("name", "张三")putInt("age", 22)putString("sex", "男")putString("height", "175cm")}msg.data = bundlemsg.replyTo = mClientMessengertry {messenger.send(msg)} catch (e: RemoteException) {e.printStackTrace()}}}private val mClientMessenger = Messenger(object : Handler(Looper.getMainLooper()) {override fun handleMessage(msg: Message) {when(msg.what){MSG_SERVER -> {Log.d("MainActivity", "currentThread ->" + Thread.currentThread().name)Log.d("MainActivity", "server callback = " + msg.data.getString("msg"))}}super.handleMessage(msg)}})override fun onDestroy() {super.onDestroy()unbindService(mServiceConnection)}
}

AIDL 使用唯一的差异是将 onServiceConnected()IBinder 参数来构建一个 Messenger 实例。

最后 Messenger 是通过 send() 方法把 msg 发送出去的,感觉就好像是使用 Handler 发送了消息一样, 真正的数据需要保存到 Bundle 里,这里需要提一点的是如果想给 Bundle 里塞实现了 Parcelable 的对象,会在服务端接受参数时爆出 ClassNotFoundException, 因为两个 App 的 Bundle 是不同类加载器加载的,所以我们使用的时候还是把基本类型数据塞到 bundle 里,这对于大量的复杂数据并不是一个好方式。

因此基于消息机制的 Messenger 虽然给我们带来了比 AIDL 更便捷的使用和理解,但并不适合管理复杂数据的跨进程通信。

可以看到上面的代码还给msg.replyTo赋值了一个 mClientMessenger , 这个就是客户端接受消息的 messenger,还记得在服务端回调给客户端的操作就是 msg.replyTo.send(replyMsg),客户端的 messenger 消息处理也是一样的。

我们在服务端和客户端都打印了一下线程,最后发现接受消息时都已经处于主线程了,这和 messenger 构造函数的 handler 入参有关,handler 指定了主线程的 Looper,那么接收消息时就是在主线程。

Messenger 原理

在创建 Messenger 时总是会传入一个 Handler 实例:

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

再进入 Handler 看下:

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 里的 MessengerImpl 继承了 IMessenger.Stub,看下 IMessenger

// IMessenger.aidl
oneway interface IMessenger {void send(in Message msg);
}

这完全就是一个标准的 aidl 接口,只是系统帮我们做了这一层的封装,然后通过 Handler 转化为我们熟悉的消息机制。值得注意这里有 oneway 关键字,说明了 Messenger 就是异步调用,因此 send() 方法不需要返回值。 还有就是参数 Message,既然它能跨进程传递,那么它一定是实现了 Parcelable 接口了,且声明了 aidl 接口:

// 包路径:/frameworks/base/core/java/android/os/Message.aidl
package android.os;
parcelable Message;// 包路径:/frameworks/base/core/java/android/os/Message.java
public final class Message implements Parcelable {//...
}

还有一个点,Messenger 实现相互通信的方式是设置一个 msg.replyTo 这个变量也是一个 Messenger, 还记得 AIDL 介绍过支持 aidl 接口的传递,所以客户端的 mClientMessenger 能够传递给服务端,然后调用 send() 方法即可回传数据。

总结

  • Messenger 是基于消息机制的跨进程通信,原理就是对 AIDL 的封装。

  • Messenger不太适合管理复杂数据的跨进程传递。

  • Messenger 天然支持异步的跨进程调用。

  • 无论是客户端还是服务端接收跨进程消息时都处于主线程,免去了切换线程步骤。

注:本文内容转载自 Messenger 跨进程通信

相关文章:

使用 Messenger 跨进程通信

什么是Messenger Messenger 也是IPC的方案之一&#xff0c;是基于消息的跨进程通信。基于消息是什么意思&#xff1f;Handler是我们最常用的消息机制&#xff0c;所以 Messenger 对于使用者来说就像是使用 Handler。实际上 Messenger 就是 AIDL 的上层封装而已&#xff0c;它们…...

Spring Cloud Gateway

路由谓词工厂 Route Predicate Factory 1. The After Route Predicate Factory spring:cloud:gateway:routes:- id: after_routeuri: https://example.orgpredicates:- After2017-01-20T17:42:47.789-07:00[America/Denver]# 用日期时间匹配 2. The Before Route Pr…...

JVM 优化技术

文章目录 JVM 优化技术概述方法内联优化说明优点内联条件 栈帧之间数据共享说明优点栈帧之间数据共享条件 JVM 优化技术 概述 JVM常见的优化技术&#xff1a; 方法内联优化。栈帧之间数据共享。 方法内联优化 说明 方法内联&#xff08;Method Inlining&#xff09;是JVM…...

【MySQL系列】- MySQL自动备份详解

【MySQL系列】- MySQL自动备份详解 文章目录 【MySQL系列】- MySQL自动备份详解一、需求背景二、Windows mysql自动备份方法2.1 复制date文件夹备份实验备份环境创建bat直接备份脚本 2 .2 mysqldump备份成sql文件创建mysqldump备份脚本 2 .3 利用WinRAR对MySQL数据库进行定时备…...

指针笔试题讲解-----让指针简单易懂(2)

目录 回顾上篇重点 &#xff1a; 一.笔试题 ( 1 ) 二.笔试题 ( 2 ) 科普进制知识点 (1) 二进制 (2) 八进制 (3)十六进制 三.笔试题&#xff08; 3 &#xff09; 四.笔试题&#xff08; 4 &#xff09; 五.笔试题&#xff08; 5 &#xff09; 六.笔试题&#xff08; …...

使用windbg分析dump文件的方法

https://zhuanlan.zhihu.com/p/613434365 一般操作如下&#xff1a; 准备工作。 打开dump文件。指定符号表文件的路径。指定可执行文件的路径。指定源码文件的路径。在windbg的命令行&#xff0c;输入并执行如下命令 .reload&#xff0c;重新加载前述数据文件。!analyze -v&a…...

【论文阅读 07】Anomaly region detection and localization in metal surface inspection

比较老的一篇论文&#xff0c;金属表面检测中的异常区域检测与定位 总结&#xff1a;提出了一个找模板图的方法&#xff0c;使用SIFT做特征提取&#xff0c;姿态估计看差异有哪些&#xff0c;Hough聚类做描述符筛选&#xff0c;仿射变换可视化匹配图之间的关系&#xf…...

SSM - Springboot - MyBatis-Plus 全栈体系(十一)

第二章 SpringFramework 五、Spring AOP 面向切面编程 6. Spring AOP 基于 XML 方式实现&#xff08;了解&#xff09; 6.1 准备工作 加入依赖和基于注解的 AOP 时一样。准备代码把测试基于注解功能时的 Java 类复制到新 module 中&#xff0c;去除所有注解。 6.2 配置 Sp…...

深度剖析贪心算法:原理、优势与实战

概述 贪心算法是一种通过每一步的局部最优选择来寻找整体最优解的方法。在每个步骤中&#xff0c;贪心算法选择当前状态下的最佳选项&#xff0c;而不考虑未来可能的影响。尽管它不能保证一定能找到全局最优解&#xff0c;但贪心算法通常简单且高效&#xff0c;适用于许多实际…...

Docker搭建DNS服务器--use

前言 DNS服务器是(Domain Name System或者Domain Name Service)域名系统或者域名服务,域名系统为Internet上的主机分配域名地址和IP地址。 安装 2.1 实验环境 IP 系统版本 角色 192.168.40.121 Ubuntu 22.10 DNS服务器 192.168.40.122 Ubuntu 22.10 测试机器 2.2 …...

“顽固”——C语言用栈实现队列

解题图解&#xff1a; 1、 先用stack1存储push来的数据 2、每当要pop数据时&#xff0c;从stack2中取&#xff0c;如果 stack2为空&#xff0c;就先从stack1中“倒”数据到stack2。 这就是用栈实现队列的基本操作 这道题看起来比较容易&#xff0c;但是&#xff01;如果你用C语…...

linux内网渗透

一、信息收集 主机发现&#xff1a; nmap -sP 192.168.16.0/24 端口探测 masscan -p 1-65535 192.168.16.168 --rate1000 开放端口如下 nmap端口详细信息获取 nmap -sC -p 8888,3306,888,21,80 -A 192.168.16.168 -oA ddd4-port目录扫描 gobuster dir…...

还没用熟 TypeScript 社区已经开始抛弃了

根据 rich-harris-talks-sveltekit-and-whats-next-for-svelte 这篇文章的报道&#xff0c; Svelte 计划要把代码从 TS 换到 JS 了。 The team is switching the underlying code from TypeScript to JavaScript. That and the update will then allow the team to incorporate…...

2023年9月19日

2> 完成文本编辑器的保存工作 头文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QFontDialog> #include <QMainWindow> #include <QFont> #include <QMessageBox> #include <QDebug> #include <QColorDialog> #include &l…...

PowerDesigner 与 mysql 同步数据

PowerDesigner 连接上数据库 创建数据库表 table_5 选择&#xff1a; 点击确认后弹出 点击run执行 刷新数据库表&#xff0c;已创建成功 修改测试表1&#xff0c;新增一个字段 取消全选 选择数据库&#xff0c;勾选修改的表&#xff0c;如果全部勾选的话&#xff0c;就…...

[python 刷题] 271 Encode and Decode Strings

[python 刷题] 271 Encode and Decode Strings 题目&#xff1a; Design an algorithm to encode a list of strings to a string. The encoded string is then sent over the network and is decoded back to the original list of strings. Machine 1 (sender) has the func…...

[QT]day3

1.一个闹钟 widget.cpp: #include "widget.h" #include "ui_widget.h"#include <QWidget> #include <QTimerEvent> //定时器事件处理类 #include <QTime>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {//给播…...

《PostgreSQL事务管理深入解析》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…...

深度分析Oracle中的NULL

【squids.cn】 全网zui低价RDS&#xff0c;免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等 关键点 特殊值NULL意味着没有数据&#xff0c;它声明了该值是未知的事实。默认情况下&#xff0c;任何类型的列和变量都可以取这个值&#xff0c;除非它们有一个NOT N…...

Python入门教学——类和对象

目录 一、面向过程和面向对象 1、面向过程 2、面向对象 二、类 三、类对象与类属性 1、类对象 2、类属性 四、类方法与静态方法 1、类方法 2、静态方法 一、面向过程和面向对象 1、面向过程 是一种以过程为中心的编程思想&#xff0c;强调事件的流程和顺序。思想&…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

boost::filesystem::path文件路径使用详解和示例

boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类&#xff0c;封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解&#xff0c;包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...

【版本控制】GitHub Desktop 入门教程与开源协作全流程解析

目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork&#xff08;创建个人副本&#xff09;步骤 2: Clone&#xff08;克隆…...