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行业感兴趣的新人

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

资料⬅专栏获取

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis数据库的删除和安装

Redis数据库的删除和安装 1、删除Redis数据库2、下载Redis数据库 1、删除Redis数据库 没有下载过的,可以直接跳到下面的安装过程↓ 我们电脑中如果有下载过Redis数据库,要更换版本的话,其实Redis数据库的删除是比较简单的,打开我…

leetcode 二分查找·系统掌握 第一个错误版本

题意&#xff1a; 题解&#xff1a; 就是经典的~01~泛型查找&#xff0c;而且一定存在这样错误的版本所以查找不会"失败"&#xff0c;返回每次查找结果即可。 int firstBadVersion(int n) {long l1,rn,mid;while(l<r){mid(lr)>>1;if(isBadVersion(mid))r…

微积分-导数1(导数与变化率)

切线 要求与曲线 C C C相切于 P ( a , f ( a ) ) P(a, f(a)) P(a,f(a))点的切线&#xff0c;我们可以在曲线上找到与之相近的一点 Q ( x , f ( x ) ) Q(x, f(x)) Q(x,f(x))&#xff0c;然后求出割线 P Q PQ PQ的斜率&#xff1a; m P Q f ( x ) − f ( a ) x − a m_{PQ} \…

csdn上传源码资源卖钱能买房买车吗?每天最高收入200-500?

csdn上传源码卖钱能买房买车吗,最高收入200-500&#xff1f; 作者收入日榜 不***孩 收益617.32元 程***妍 收益534.56元 s***n 收益323.71元 盈***客 收益315.05元 极***计 收益284.17元

[第五空间2019 决赛]PWN5

参考文章: 格式化字符串漏洞原理及其利用&#xff08;附带pwn例题讲解&#xff09;_格式化字符串攻击教程-CSDN博客 格式化字符串漏洞原理详解_静态编译 格式化字符串漏洞-CSDN博客 BUU pwn [第五空间2019 决赛]PWN5 //格式化字符串漏洞 - Nemuzuki - 博客园 (cnblogs.com) …

如果申请小程序地理位置接口权限之前刷到这一篇就好了

小程序地理位置接口有什么功能&#xff1f; 通常情况下&#xff0c;我们在开发小程序时&#xff0c;可能会用到获取用户地理位置信息的功能。小程序开发者开放平台的新规定指出&#xff0c;如果没有申请开通微信小程序地理位置接口&#xff08;getLocation&#xff09;&#xf…

【建议收藏】Android中高级大厂面试源码秘籍,为你备战2021金三银四,直通大厂

首先来说下为什么要读源码&#xff0c;有学习源码的必要吗&#xff1f; 为什么要阅读源码&#xff1f; 关于为什么阅读和学习源码&#xff0c;我个人认为可能有以下几点&#xff1a; &#xff08;一&#xff09;吊打面试官&#xff0c;应对面试 为了找到更好的工作&#xff…

【小沐学AI】Python实现语音识别(Whisper-Web)

文章目录 1、简介2、下载2.1 openai-whisper2.2 whisper-web 结语 1、简介 https://openai.com/index/whisper/ Whisper 是一种自动语音识别 &#xff08;ASR&#xff09; 系统&#xff0c;经过 680,000 小时的多语言和多任务监督数据的训练&#xff0c;从网络上收集。我们表…

Jenkins定时构建自动化(一):Jenkins下载安装配置

目录 ​编辑 一、jdk下载安装 1. 已下载安装jdk 2. 未下载安装jdk 二、jenkins安装 1. .war包安装 三、获取IP地址 四、jenkins网页配置 一、jdk下载安装 1. 已下载安装jdk &#xff08;1&#xff09;查询jdk版本命令&#xff1a;java -version &#xff08;2&#xff09;…

4.XSS-反射型(get)利用:获取cookie

GET反射型XSS利用&#xff1a;获取cookie 修改一下配置文件\pikachu\pkxss\xcookie\cookie.php 我这里将对应的IP地址修改为本地pikachu的主站IP地址&#xff0c;这样给用户造成一种正常视觉上的欺骗&#xff0c;容易上当。重定向到pikachu主页面 基于IP搭建的pkxss平台(入侵…

Python 函数注解,给函数贴上小标签

目录 什么是函数注解? 为什么使用函数注解? 如何编写函数注解? 实战演练 与类型提示(Type Hints)的关系 类型安全的运算器 什么是函数注解? 函数注解(Function Annotations)是Python 3中新增的一个特性,它允许为函数的参数和返回值指定类型。 这些注解不会改变…

高速缓存存储器(Chche)

为了解决CPU和主存之间速度不匹配的问题&#xff0c;计算机系统中引入了高速缓存&#xff08;Chche&#xff09;的概念。 基本想法&#xff1a;使用速度更快但容量更小、价格更高的SRAM制作一个缓冲存储器&#xff0c;用来存放经常用到的信息&#xff1b;这样一来&#xff0c;…

【Git】--Part4--多人协作

在之前的Git博客中&#xff0c;已经把Git本地相关的操作以及远程操作的介绍完了。如下&#xff1a; Git–Part1–基础操作 - 掘金 (juejin.cn)Git–Part2–分支管理 - 掘金 (juejin.cn)Git–Part3–远程操作 & 配置 & 标签管理 - 掘金 (juejin.cn) 这篇文章会介绍两种…

[FreeRTOS 基础知识] 互斥访问与回环队列 概念

文章目录 为什么需要互斥访问&#xff1f;使用队列实现互斥访问休眠和唤醒机制环形缓冲区 为什么需要互斥访问&#xff1f; 在裸机中&#xff0c;假设有两个函数&#xff08;func_A, func_B&#xff09;都要修改a的值&#xff08;a&#xff09;&#xff0c;那么将a定义为全局变…

音视频的Buffer处理

最近在做安卓下UVC的一个案子。正好之前搞过ST方案的开机广告&#xff0c;这个也是我少数最后没搞成功的项目。当时也有点客观原因&#xff0c;当时ST要退出机顶盒市场&#xff0c;所以一切的支持都停了&#xff0c;当时啃他家播放器几十万行的代码&#xff0c;而且几乎没有文档…

C++ | Leetcode C++题解之第179题最大数

题目&#xff1a; 题解&#xff1a; class Solution { public:string largestNumber(vector<int> &nums) {sort(nums.begin(), nums.end(), [](const int &x, const int &y) {return to_string(x) to_string(y) > to_string(y) to_string(x);});if (nu…

Django框架数据库ORM查询操作

Django框架在生成数据库的models模型文件后&#xff0c;旧可以在应用中通过ORM来操作数据库了。今天抽空试了下查询语句。以下是常用的查询语句。 以下查询需要引入django的Sum&#xff0c;Count&#xff0c;Q模块 from django.db.models import Sum,Count,Q 导入生成的mode…

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

题目&#xff1a; 题解&#xff1a; class Solution:def largestNumber(self, nums: List[int]) -> str:def quick_sort(l , r):if l > r: returni, j l, rwhile i < j:while strs[j] strs[l] > strs[l] strs[j] and i < j: j - 1while strs[i] strs[l] &l…

C++系列-String(一)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” string是用于字符串&#xff0c;可以增删改查 首先&#xff0c;我们来看一下string的底层 接下来&#xff0c;我们来看一下string的常用接口有哪些&#xff1a; #define _CRT_S…

csrf+xss组合拳

csrfxss组合拳 一、环境搭建 靶场cms文章管理系统 二、流程开始 这是系统前端 系统管理后台 而我们要打到后台管理员的cookie&#xff0c;结合前端存储型的xss完全可以实现&#xff0c;那后端怎么被打到cookie呢&#xff0c;我们来从这里添加用户开始分析数据包来说明 看看…