Android进程间通信 Messenger详解

//这里服务端Service是运行在单独的进程中的 android:process=“:other”

class MessengerService : Service() {

private lateinit var mMessenger: Messenger

override fun onBind(intent: Intent): IBinder {

log(TAG, “onBind~”)

//传入Handler实例化Messenger

mMessenger = Messenger(IncomingHandler(this))

//将Messenger中的binder返回给客户端,让它可以远程调用

return mMessenger.binder

}

//处理客户端传递过来的消息(Message) 并根据what决定下一步操作

internal class IncomingHandler(

context: Context,

private val applicationContext: Context = context.applicationContext

) : Handler(

Looper.getMainLooper()

) {

override fun handleMessage(msg: Message) {

when (msg.what) {

MSG_SAY_HELLO -> {

Toast.makeText(applicationContext, “hello!”, Toast.LENGTH_SHORT).show()

log(TAG, “hello!”)

}

else -> super.handleMessage(msg)

}

}

}

}

2.2.2 客户端

客户端进程中,首先是需要绑定远程Service.绑定完成之后,在onServiceConnected()中拿到远程Service返回的IBinder对象,用此IBinder对象实例化客户端这边的Messenger.有了这个Messenger,就可以通过这个Messenger往服务端发送消息了.示例代码如下:

class MessengerActivity : TitleBarActivity() {

/** 与服务端进行沟通的Messenger */

private var mService: Messenger? = null

/** 是否已bindService */

private var bound: Boolean = false

private val mServiceConnection = object : ServiceConnection {

override fun onServiceConnected(name: ComponentName?, service: IBinder?) {

mService = Messenger(service)

bound = true

}

override fun onServiceDisconnected(name: ComponentName?) {

mService = null

bound = false​

}

}

override fun getThisTitle(): CharSequence {

return “Messenger”

}

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_messenger)

btnConnect.setOnClickListener {

connectService()

}

btnSayHello.setOnClickListener {

sayHello()

}

}

private fun sayHello() {

if (!bound) {

return

}

//创建,并且发送一个message给服务端 Message中what指定为MSG_SAY_HELLO

val message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)

try {

mService?.send(message)

} catch (e: RemoteException) {

e.printStackTrace()

}

}

private fun connectService() {

Intent().apply {

action = “com.xfhy.messenger.Server.Action”

setPackage(“com.xfhy.allinone”)

}.also { intent ->

bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)

}

}

override fun onStop() {

super.onStop()

if (bound) {

unbindService(mServiceConnection)

bound = false

}

}

}

通过示例代码我们知道客户端通过Messenger与服务端进行通信时,必须将数据放入Message中,Messenger和Message都实现了Parcelable接口,因此是可以跨进程传输的.Message只能通过what、arg1、arg2、Bundle以及replyTo来承载需要传递的数据,如果需要传递Serializable或者Parcelable的对象则可以放进Bundle里面进行传递,Bundle还支持其他大量的数据类型.

2.2.3 服务端向客户端发送消息

有时候我们需要客户端能响应服务端发送的消息,此时我们只需要在上面的示例的基础上简单修改即可.

服务端这边每次收到消息,都回复一条消息给客户端,方便测试

internal class IncomingHandler : Handler(Looper.getMainLooper()) {

override fun handleMessage(msg: Message) {

when (msg.what) {

MSG_SAY_HELLO -> {

log(TAG, “hello!”)

//客户端的Messenger就是放在Message的replyTo中的

replyToClient(msg, “I have received your message and will reply to you later”)

}

MSG_TRANSFER_SERIALIZABLE -> log(TAG, “传递过来的对象: ${msg.data?.get(“person”)}”)

else -> super.handleMessage(msg)

}

}

private fun replyToClient(msg: Message, replyText: String) {

val clientMessenger = msg.replyTo

val replyMessage = Message.obtain(null, MSG_FROM_SERVICE)

replyMessage.data = Bundle().apply {

putString(“reply”, replyText)

}

try {

clientMessenger?.send(replyMessage)

} catch (e: RemoteException) {

e.printStackTrace()

}

}

}

而客户端这边需要做出响应,则还需在客户端创建一个Messenger,并为其创建一个Handler用于接收服务端传递过来的消息.在客户端发送消息时,需要将Message#replyTo设置为客户端的Messenger. 服务端拿到这个Messanger才能回复消息.

/** 客户端这边的Messenger */

private var mClientMessenger = Messenger(IncomingHandler())

class IncomingHandler : Handler(Looper.getMainLooper()) {

override fun handleMessage(msg: Message) {

when (msg.what) {

MSG_FROM_SERVICE -> {

log(TAG, “Received from service: ${msg.data?.getString(“reply”)}”)

}

else -> super.handleMessage(msg)

}

}

}

private fun sayHello() {

if (!bound) {

return

}

//创建,并且发送一个message给服务端 Message中what指定为MSG_SAY_HELLO

val message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)

//注意 这里是新增的

message.replyTo = mClientMessenger

message.data = Bundle().apply {

putSerializable(“person”, SerializablePerson(“张三”))

}

try {

mService?.send(message)

} catch (e: RemoteException) {

e.printStackTrace()

}

}

服务端调用sayHello()之后,输出日志如下:

2020-12-31 11:59:40.420 29702-29702/com.xfhy.allinone D/xfhy_messenger: hello!

2020-12-31 11:59:40.421 29649-29649/com.xfhy.allinone D/xfhy_messenger: Received from service: I have received your message and will reply to you later

日志里面明显看到是2个进程,所以现在是达到是双向通信的目的.Messenger的使用大概就是这些了,下面是Messenger的大致工作原理图

//todo xfhy 插图 Messenger的工作原理 Android开发艺术探索(P93)

3. 原理


3.1 客户端->服务端通信

服务端

当客户端到服务端单向通信时,我们来看一下大致的原理.首先是服务端这边在onBind方法中返回了Messenger的binder对象

override fun onBind(intent: Intent): IBinder {

//传入Handler实例化Messenger

mMessenger = Messenger(IncomingHandler())

//将Messenger中的binder返回给客户端,让它可以远程调用

return mMessenger.binder

}

我们看下Messenger里面的binder是什么:

private final IMessenger mTarget;

public Messenger(Handler target) {

mTarget = target.getIMessenger();

}

public Messenger(IBinder target) {

mTarget = IMessenger.Stub.asInterface(target);

}

public void send(Message message) throws RemoteException {

mTarget.send(message);

}

public IBinder getBinder() {

return mTarget.asBinder();

}

从Messenger的构造方法(IMessenger.Stub.asInterface())可以看出它底层应该是使用的AIDL搞的.getBinder()其实是将调用了mTarget.asBinder(),而mTarget是我们传进来的Handler里面拿出来的,跟进Handler.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);

}

}

原来IMessenger是Handler的内部类MessengerImpl,它只有一个send方法.结合上面Messenger的源码,我们发现调用Messenger的send方法其实就是调用这里的MessengerImpl的send方法,然后这个send里面将Message转发给Handler#sendMessage(),最后也就是去了Handler#handleMessage()里面接收到这个Message.

MessengerImpl是继承自IMessenger.Stub,这一看就感觉是AIDL文件自动生成的嘛,easy.大胆猜测一下对应的aidl文件应该是IMessenger.aidl,我们去源码里面找IMessenger.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);

}

根据aidl文件,它自动生成的IMessenger.java应该长下面这样:

package android.os;

public interface IMessenger extends android.os.IInterface {

/**

  • Local-side IPC implementation stub class.

*/

public static abstract class Stub extends android.os.Binder implements IMessenger {

private static final java.lang.String DESCRIPTOR = “android.os.IMessenger”;

/**

  • Construct the stub at attach it to the interface.

*/

public Stub() {

this.attachInterface(this, DESCRIPTOR);

}

/**

  • Cast an IBinder object into an android.os.IMessenger interface,

  • generating a proxy if needed.

*/

public static IMessenger asInterface(android.os.IBinder obj) {

if ((obj == null)) {

return null;

}

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

if (((iin != null) && (iin instanceof IMessenger))) {

return ((IMessenger) iin);

}

return new IMessenger.Stub.Proxy(obj);

}

@Override

public android.os.IBinder asBinder() {

return this;

}

@Override

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {

java.lang.String descriptor = DESCRIPTOR;

switch (code) {

case INTERFACE_TRANSACTION: {

reply.writeString(descriptor);

return true;

}

case TRANSACTION_send: {

data.enforceInterface(descriptor);

android.os.Message _arg0;

if ((0 != data.readInt())) {

_arg0 = android.os.Message.CREATOR.createFromParcel(data);

} else {

_arg0 = null;

}

this.send(_arg0);

return true;

}

default: {

return super.onTransact(code, data, reply, flags);

}

}

}

private static class Proxy implements IMessenger {

private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {

mRemote = remote;

}

@Override

public android.os.IBinder asBinder() {

return mRemote;

}

public java.lang.String getInterfaceDescriptor() {

return DESCRIPTOR;

}

@Override

public void send(android.os.Message msg) throws android.os.RemoteException {

android.os.Parcel _data = android.os.Parcel.obtain();

try {

_data.writeInterfaceToken(DESCRIPTOR);

if ((msg != null)) {

_data.writeInt(1);

msg.writeToParcel(_data, 0);

} else {

_data.writeInt(0);

}

mRemote.transact(Stub.TRANSACTION_send, _data, null, android.os.IBinder.FLAG_ONEWAY);

} finally {

_data.recycle();

}

}

}

static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

}

public void send(android.os.Message msg) throws android.os.RemoteException;

}

这就好办了,这就明摆着说明Messenger底层是基于AIDL实现的.服务端这边这条线: Service#onBind()->mMessenger.getBinder()->Handler#getIMessenger()->MessengerImpl(IMessenger.Stub),其实就是和我们使用AIDL一样将IXXX.Stub的子类通过onBind返回回去,客户端绑定的时候好拿到binder对象.接收客户端的消息时,是通过MessengerImpl转发给Handler来完成的,服务端这边定义的那个Handler就可以在handleMessage()中处理跨进程传递过来的Message,从而理解客户端想要调用什么服务,然后执行相应的逻辑.

客户端

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
T_CALL_TRANSACTION + 0);

}

public void send(android.os.Message msg) throws android.os.RemoteException;

}

这就好办了,这就明摆着说明Messenger底层是基于AIDL实现的.服务端这边这条线: Service#onBind()->mMessenger.getBinder()->Handler#getIMessenger()->MessengerImpl(IMessenger.Stub),其实就是和我们使用AIDL一样将IXXX.Stub的子类通过onBind返回回去,客户端绑定的时候好拿到binder对象.接收客户端的消息时,是通过MessengerImpl转发给Handler来完成的,服务端这边定义的那个Handler就可以在handleMessage()中处理跨进程传递过来的Message,从而理解客户端想要调用什么服务,然后执行相应的逻辑.

客户端

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-CtEVRTG6-1719085829779)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/32502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang | Leetcode Golang题解之第174题地下城游戏

题目&#xff1a; 题解&#xff1a; func calculateMinimumHP(dungeon [][]int) int {n, m : len(dungeon), len(dungeon[0])dp : make([][]int, n 1)for i : 0; i < len(dp); i {dp[i] make([]int, m 1)for j : 0; j < len(dp[i]); j {dp[i][j] math.MaxInt32}}dp[…

mysql数据库切换成kingbase(人大金仓)数据库时遇到的字段不存在问题

一、问题描述 mysql数据库切换成国产数据库人大金仓&#xff08;kingbase&#xff09;数据库的遇到的字段不存在的问题,根本原因其实是没有找到相对应的表&#xff0c;报错示例如下图所示&#xff1a; 二、问题解决 1、如果所有的表都发生上述的错误&#xff0c;kingbase的…

基于matlab的不同边缘检测算子的边缘检测

1 原理 1.1 边缘检测概述 边缘检测是图像处理和计算机视觉中的基本问题&#xff0c;其目的在于标识数字图像中亮度变化明显的点。这些变化通常反映了图像属性的重要事件和变化&#xff0c;如深度不连续、表面方向不连续、物质属性变化和场景照明变化等。边缘检测在特征提取中…

OSPF 2类LSA详解

概述 上图为2类LSA : Network LSA 的报文格式 , 我们重点关注3个报文字段即可 , 其他内容没有实际的信息 Link State ID : DR的接口IP地址 Network Mask : 该MA网络的掩码 Attached Router : 连接在该MA网络的所有路由器的Router ID 2类LSA一定是DR产生的 , 关于OSPF DR的细节…

数据结构之二叉树的超详细讲解(3)--(二叉树的遍历和操作)

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 数据结构之二叉树的超详细讲解(3)--(二叉树链式结构的实现) 收录于专栏【数据结构初阶】 本专栏旨在分享学习数据结构学习的一点学习笔记&#xff0c;欢迎大家在…

展讯-QMI8658和气压传感器驱动调试

1.调试QMI8658 参考demo&#xff0c;添加QMI8610相关内容 当前驱动路径位于&#xff1a;bsp/modules/input/misc/qmi8610/qmi8610.c 编译使用make sockoimage 用fastboot烧录 1.确定驱动被正常加载 代码添加之后&#xff0c;首先确定有没有进入当前驱动文件 dmesg |grep …

如何用GO语言实现冒泡排序算法?

本章教程,介绍一下如何用GO语言实现基础排序算法中的冒泡排序。 一、程序代码 package mainimport ("fmt""math/rand""time" )// bubbleSort 函数实现冒泡排序算法 func bubbleSort(arr []int) {n

32 - 判断三角形(高频 SQL 50 题基础版)

32 - 判断三角形 select *,if(xy>z and xz>y and zy > x,Yes,No) triangle fromTriangle;

Day9 —— 大数据技术之ZooKeeper

ZooKeeper快速入门系列 ZooKeeper的概述什么是ZooKeeper&#xff1f;ZooKeeper的特点和功能使用ZooKeeper的原因 ZooKeeper数据模型ZooKeeper安装ZooKeeper配置ZooKeeper命令行操作常见服务端命令 ZooKeeper的概述 什么是ZooKeeper&#xff1f; ZooKeeper是一个开源的分布式协…

jarvisoj_level2

首先检查checksec 可以看见no canary found 说明可能是栈溢出 运行一下程序 随便输一个,得到hello world(感觉这一步好像没啥用,没有RE那用) IDA 32 打开 后门 漏洞点 buf: 代码思路:利用溢出,把buf填满,然后构造程序,得到获取shell的目的 exp来自 BUUCTF—jarvisoj_level…

【Qt】学习Day1

文章目录 Qt简介创建第一个Qt程序创建过程介绍main函数工程文件头文件控件源文件快捷键按钮控件常用API对象树坐标系 信号和槽自定义信号自定义槽函数触发自定义的信号案例-下课后&#xff0c;老师触发饿了信号&#xff0c;学生响应信号&#xff0c;请客吃饭重载信号连接信号La…

Paxos分布式共识算法

Paxos分布式共识算法 一、简介 Paxos算法是由莱斯利兰伯特(Leslie Lamport)于1990年提出的一种基于消息传递且具有高度容错特性的一致性算法。它主要用于解决分布式系统中如何就某个值达成一致&#xff0c;并保证整个系统的一致性&#xff0c;即使在部分节点发生故障的情况下…

论文浅尝 | 基于生成模型的多模态实体链接

笔记整理&#xff1a;郭凌冰&#xff0c;浙江大学博士&#xff0c;研究方向为知识图谱 链接&#xff1a;https://arxiv.org/abs/2306.12725 1. 动机 多模态实体链接&#xff08;Multimodal Entity Linking&#xff0c;MEL&#xff09;旨在将带有多模态上下文的提及映射到知识库…

[SAP ABAP] 运算符

1.算数运算符 算术运算符描述加法-减法*乘法/除法MOD取余 示例1 输出结果: 输出结果: 2.比较运算符 比较运算符描述示例 等于 A B A EQ B <> 不等于 A <> B A NE B >大于 A > B A GT B <小于 A < B A LT B >大于或等于 A > B A GE B <小…

keep-alive实现页面缓存

<!--router-view 实现页面缓存--> <router-view v-slot"{Component}"><keep-alive :max"8"><component :is"Component"></component></keep-alive> </router-view>

2024年6月23日 十二生肖 今日运势

小运播报&#xff1a;2024年6月23日&#xff0c;星期日&#xff0c;农历五月十八 &#xff08;甲辰年庚午月戊午日&#xff09;&#xff0c;法定节假日。今天国际奥林匹克日&#xff0c;坚不可摧的意志&#xff0c;披荆斩棘的豪情&#xff0c;永远值得拥有&#xff01; 红榜生…

算法金 | 统计学的回归和机器学习中的回归有什么差别?

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 统计学中的回归 目标&#xff1a; 主要用于解释和推断自变量&#xff08;independent variables&#xff09;和因变量&#xff08;de…

array_key_exists() expects parameter 2 to be array, null given

公众号获取微信服务器IP地址 错误代码如下 public function getwxIP(){//获取微信服务器IP地址$accessToken $this->getwxoaiAccessToken();$userToken new UserToken();$result $userToken->curl_get("https: //api.weixin.qq.com/cgi-bin/get_api_domain_ip…

Day58 代码随想录打卡|二叉树篇---将有序数组转换为二叉搜索树

题目&#xff08;leecode T108&#xff09;&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 方法&#xff1a;用有序数组构造平衡二叉搜索树&#xff0c;和我们之前有一题的思路差不多&#xff0c…

遗传算法求解时间窗车辆路径规划问题(附python代码)

摘要 本研究提出了一种基于遗传算法的车辆路径规划&#xff08;VRP&#xff09;问题求解框架&#xff0c;它能够有效地处理一系列复杂约束&#xff0c;包括软时间窗、硬时间窗、行驶距离限制、车辆最大载重量、多个配送中心的协调、特定的配送顺序&#xff0c;以及多种车型的选…