Android进程间通信 Messenger详解

news2025/3/10 21:22:45

//这里服务端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.coloradmin.cn/o/1851944.html

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

相关文章

Android测量

最大模式(MeasureSpec.AT_MOST) 这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。 最高两位是11的时候表示”最大模式”。即MeasureSpec.AT_MOST未指定模式(…

Java | Leetcode Java题解之第179题最大数

题目: 题解: class Solution {public String largestNumber(int[] nums) {int n nums.length;// 转换成包装类型,以便传入 Comparator 对象(此处为 lambda 表达式)Integer[] numsArr new Integer[n];for (int i 0;…

windows git配置多个账号

window下git多账号配置_百度搜索 (baidu.com) 最重要的是这里生成新的id_rsa文件的时候,bash窗口是在 .ssh路径下 其实就是这个窗口在什么路径下执行的就是生成在什么路径 下面窗口路径不对,不是Desktop,应该是.ssh 如果是Desktop或者任何一…

如何解决跨区域文件传输存在的安全管控问题?

⼤型企业和集团为扩⼤市场份额、优化资源配置,会在不同地区设⽴多级下属分⽀机构、研发中心、实验室等,存在研发数据横向或纵向流转的需求,研发数据进行跨区域文件传输的场景。跨区域可能是网络区域,也可能是地理区域,…

常见数字化转型方案撰写的思维模式

通过这一段时间的学习和倾听,结合DAMA数据管理知识体系学习与项目实践,对大部分数据治理类项目、信息化建设和数字化转型项目的思维模式做了一些总结梳理,具体有如下四种,供参考。 一、方法1:结合环境六边形法 1.要点题,弄清楚问题是什么 2.目标原则有哪些,补充哪些 3.…

Android O 适配详细指南

NotificationChannel channel new NotificationChannel(mChannelId, name, NotificationManager.IMPORTANCE_DEFAULT); mNotificationManager.createNotificationChannel(channel); } } // 创建通知传入channelId NotificationCompat.Builder builder new NotificationCompat…

【Linux详解】缓冲区优化 | 进度条的实现 | Linux下git 的上传

目录 一. 缓冲区 1. 缓冲区概念 2. 缓冲区作用 2.1 提升读写效率 2.2 减少等待时间 3. 缓冲区刷新策略 3.4 特殊策略 4. 缓冲区存储位置 5. 总结 二. 实现进度条 引入:倒计时 process.c 三. Linux下git的上传 sum 一. 缓冲区 1. 缓冲区概念 缓冲区是…

C++实现自动生成c++类中的属性的get和set方法

目录 应用场景 运行准备 代码展示 结果显示 应用场景 当我们在编写类的属性时,需要对该属性进行封装,需要一系列的get和set的方法。例如下面是天气类的成员属性。可以看到属性很多,而写get和set都是一些固定的操作,因此可以直…

Swift 中的动态数组

Swift 的 Array 类型是一种强大而灵活的集合类型,可以根据需要自动扩展或缩减其容量。 动态数组的基本概念 Swift 中的数组是基于动态数组(dynamic array)的概念实现的。动态数组能够根据需要自动调整其容量,以容纳新增的元素&a…

51单片机STC89C52RC——6.3 定时器/计数器 实现计时功能(定时器+中断系统+LCD1602液晶显示器)

目录 目的/效果 一,STC单片机模块 二,定时器 中断系统LCD1602显示 三,创建Keil项目 四,代码 五,代码编译、下载到51单片机 ​ 目的/效果 用定时器实现系统中断,计时信息显示在LCD1602上。效果如下 …

计算机组成原理----指令系统课后习题

对应的知识点: 指令系统 扩展操作码的计算: 公式: 对扩展操作码而言,若地址长度为n,上一层留出m种状态,下一层可扩展出 mx2^n 种状态 1.设计某指令系统时,假设采用 16 位定长指令字格式&#…

文件上传漏洞-下篇

一、白名单绕过 目录路径检测绕过 00截断 简介: 0x00是字符串的结束标识符,攻击者可以利用手动添加字符串标识符的方式来将后面的内容进行截断,而后面的内容又可以帮助我们绕过检测。 饶过条件 利用操作:Pass-12 要求&#xff…

“论微服务架构及其应用”写作框架,软考高级,系统架构设计师

论文真题 论微服务架构及其应用近年来,随着互联网行业的迅猛发展,公司或组织业务的不断扩张,需求的快速变化以及用户量的不断增加,传统的单块(Monolithic)软件架构面临着越来越多的挑战,已逐渐…

前端 CSS 经典:backface-visibility 属性

前言&#xff1a;backface-visibility 属性可以使反转 180deg 的元素隐藏&#xff0c;使用这个属性实现卡片翻转效果 效果 代码实现 <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" /><meta http-equiv"X-…

以太坊==MetaMask获取测试币最新网址

估算分数https://community.infura.io/t/unable-to-receive-sepolia-eth-from-faucet/7715 Gitcoin Passport 水龙头地址&#xff0c;填入自己的测试地址 水龙头项目地址 GitHub - pk910/PoWFaucet: Modularized faucet for EVM chains with different protection methods (…

51单片机STC89C52RC——6.1 中断系统

一&#xff0c;文字层面理解 反正我看下面的几段文字时脑壳没有正常运转。一个头几个大 中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。 当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求&#xff0c;要求CPU暂停当前的工作&#xff0c;转而去处理这…

接口自动化拓展:Flask框架安装、介绍及工作中的应用!

Flask是一个轻量级的Python Web框架&#xff0c;用于构建Web应用程序和API。它简洁而灵活&#xff0c;容易上手&#xff0c;并且非常适合用于开发小型到中型规模的应用程序。在接口自动化测试中&#xff0c;Flask可以作为服务器框架&#xff0c;用于搭建测试接口。 本文将从零…

Android:知道类加载过程面试还是卡壳?干货总结,一网打净“类”的基础知识!

多线程进行类的初始化会出问题吗&#xff1f; 类的实例化触发时机。 <clinit>()方法和<init>()方法区别。 在类都没有初始化完毕之前&#xff0c;能直接进行实例化相应的对象吗? 类的初始化过程与类的实例化过程的异同&#xff1f; 一个实例变量在对象初始化…

Docker(数据卷、自定义镜像、网络)

数据卷 数据卷&#xff08;volume&#xff09;是一个虚拟目录&#xff0c;是容器内目录与宿主机目录之间映射的桥梁。 命令 说明 docker volume create 创建数据卷 docker volume ls 查看所有数据卷 docker volume rm 删除指定数据卷 docker volume inspect 查看某个…

视觉新纪元:解码LED显示屏的视角、可视角、最佳视角的最终奥秘

在璀璨夺目的LED显示屏世界里&#xff0c;每一个绚烂画面的背后&#xff0c;都离不开三个关键概念&#xff1a;视角、可视角与最佳视角。这些术语不仅是衡量显示效果的重要标尺&#xff0c;也是连接观众与精彩内容的桥梁。让我们一起走进这场视觉盛宴&#xff0c;探索那些让LED…