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

一. 概述

        Android中比较有代表性的两大通信机制:1. 线程间Handler通信   2. 进程间Binder通信,本篇文章中我们在理解AIDL原理的基础上来解读一下Messenger的源代码, 并结合示例Demo加深理解。 在看本篇文章前,建议先查阅一下笔者的 Android 进程间通信机制(六) 手写AIDL文件

        首先说下我对Messenger的个人理解:

1. 从概念上阐述

    Messenger进程间通信的信使,是一个轻量级的IPC通信方案, 和Message消息不是一个概念。

2. 从实现上描述

     实现: AIDL  +  Handler    它的底层实现原理还是使用 AIDL,  对应的文件为(IMessenger.aidl)

相应的接口方法:

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

Messenger使用了Handler进行通信, 调用的这句代码

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            //核心代码
            Handler.this.sendMessage(msg);
        }
    }

3. 使用场景

    两个进程间只进行简单的,轻量级通信, 不需要处理多线程的业务场景。

二. 示例

2.1 先看一下清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.messengertest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MessengerTest">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:process=":client">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service
            android:name=".MyService"
            android:exported="true"
            android:enabled="true"
            android:process=":server">
        </service>
    </application>

</manifest>

我把MainActivity作为客户端,  MyService作为服务端, 用android:process标记让两个组件运行在不同的进程中。

2.2 客户端

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private Messenger mServiceMessenger;

    //创建客户端 Messenger 对象,并绑定 Handler
    private Messenger mClientMessenger = new Messenger(new MessengerHandler());

    private static final class MessengerHandler extends Handler {

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MyConstants.MSG_FROM_SERVER: //接受来自 服务端 的消息
                    Log.d(TAG, "receive msg from server: " + msg.getData().get("reply"));
                    break;
                default:
                    break;
            }
        }
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //绑定服务端的 Service, 成功后用返回的 IBinder 对象创建 Messenger
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            mServiceMessenger = new Messenger(binder); // 获取服务端的 Messenger对象, 通过它向服务端发送消息
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg", "hello, this is client");
            msg.setData(data);
            msg.replyTo = mClientMessenger ; //将客户端的 Messenger 传给 服务端
            try {
                mServiceMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
}

2.3 服务端

public class MyService extends Service {

    private static final String TAG = "MyService";

    //服务端创建 Messenger对象,并绑定对应 Handler, 用于处理 Client 发过来的消息
    private final Messenger mServiceMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回 Binder 对象给 Client
        return mServiceMessenger.getBinder();
    }

    public MyService() {
    }

    private static class MessengerHandler extends Handler {

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MyConstants.MSG_FROM_CLIENT:
                    Log.d(TAG, "receive msg from client:" + msg.getData().getString("msg"));
                    Messenger clientMessenger = msg.replyTo; //获取传递过来的客户端Messenger对象
                    Message replyMsg = Message.obtain(null, MyConstants.MSG_FROM_SERVER);

                    Bundle bundle = new Bundle();
                    bundle.putString("reply", " server have receive your msg");
                    replyMsg.setData(bundle);

                    try {
                        clientMessenger.send(replyMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

2.4 消息常量

public class MyConstants {

    public static final int MSG_FROM_SERVER = 2;
    public static final int MSG_FROM_CLIENT = 1;
}

2.5 运行结果

16:12:57.139 31928 31928 D MyService: receive msg from client:hello, this is client
16:12:57.181 31894 31894 D MainActivity: receive msg from server:  server have receive your msg

客户端和服务端关键代码处有注释说明,方便理解。

三. 模型

上面Demo,可以用如下图来说明

四. 源码解析

大多数应用,跨进程只是一对一通信,  并且无需执行多线程处理的业务, 此时使用Messenger更适合一点。

我们重点看如下5个方法:

public final class Messenger implements Parcelable {
    ......

    //1. mTarget 为 IMessenger接口实例化对象
    private final IMessenger mTarget;


    //2. 创建一个指向target Handler的Messenger,
    //   然后调运Messenger的send 实质是调用Handler的sendMessage方法
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

    //3. 跨进程发送消息方法
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

    //4. 获得Messenger的Binder,一般用在service端获取返回
    public IBinder getBinder() {
        return mTarget.asBinder();
    }

    //5. 获取getBinder相同的Messenger对象,一般用在client端获取
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

    ......

}

第1个方法  mTarget 为 IMessenger 接口实例化对象, 在java语法规则中,接口类不能直接创建对象, 只能实例化实现该接口的类对象,我们来看看IMessenger.java 它是IMessenger.aidl 文件通过aapt编译自动生成的,具体输出路径为:

out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/base/core/java/android/os/IMessenger.java

public interface IMessenger extends android.os.IInterface {
   ......
}

它是一个public 接口类,不能直接通过new IMessenge() 来创建对象,但是可以new 实现了该接口的类对象。

第2和5个方法 是Messenger类的两个构造方法

  private final IMessenger mTarget;
 
  public Messenger(Handler target) {
       mTarget = target.getIMessenger();
   }
 
  public Messenger(IBinder target) {
       mTarget = IMessenger.Stub.asInterface(target);
  }

可以这样子理解两个方法的用途: 

1. 参数为Handler的是远程端进程的实例方法

2. 参数为IBinder的是客户端进程的实例方法

第1个构造方法的内容:

//frameworks/base/core/java/android/os/Handler.java 文件中

IMessenger mMessenger;

//对于一个Handler对象来说getIMessenger得到的Messenger是一个单例模式对象
final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            //这里就是new了一个实现该接口类的对象
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }
    
    //这里其实是IMessenger.aidl的接口send的具体实现类
    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            //核心还是与该(进程绑定的Handler)去发送消息
            Handler.this.sendMessage(msg);
        }
    }

结论:Messenger类中的mTarget其实就是一个Handler中 实现了(IMessenger远程IPC send接口)的MessengerImpl 单例对象。

第2个方法的使用,就是在客户端拿到服务端的Messenger对象

在MyService.java中 我们看下Service中的onBind实现,其调运了Messenger的getBinder方法,这个方法源码如下:

    //服务端创建 Messenger对象,并绑定对应 Handler, 用于处理 Client 发过来的消息
    private final Messenger mServiceMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回 Binder 对象给 Client
        return mServiceMessenger .getBinder();
    }

    public IBinder getBinder() {
        return mTarget.asBinder();
    }
    //调用mTarget.asBinder()方法,也即是this对象自己

    //在 Stub类中
    public static abstract class Stub extends android.os.Binder implements android.os.IMessenger{
        ....
        @Override public android.os.IBinder asBinder()
        {
             return this;
        }
        ....
    }

    //还记得上面的Handler类中的 MessengerImpl 实现了 IMessenger.Stub
    IMessenger mMessenger = new MessengerImpl()
    private final class MessengerImpl extends IMessenger.Stub {

    }
    

可以看见,其实asBinder返回的就是this, 也就是把Service中的Messenger通过onBind方法返回给客户端。

然后再回到客户端 MainActivity.java 中的 onServiceConnected 方法

    private Messenger mServiceMessenger;

    private final IMessenger mTarget;

    // 1. 获取服务端的 Messenger对象, 通过它向服务端发送消息
    public void onServiceConnected(ComponentName name, IBinder binder) {
            mServiceMessenger = new Messenger(binder); 

    // 2. 上面的 new Messenger(binder)调用方法
    
    /**
     * Create a Messenger from a raw IBinder, which had previously been
     * retrieved with {@link #getBinder}.
     * 
     * @param target The IBinder this Messenger should communicate with.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

通过asInterface方法转化一下,由于不在同一个进程,会把Stub转换成Proxy代理对象

    public static android.os.IMessenger asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof android.os.IMessenger))) {
        return ((android.os.IMessenger)iin);
      }
      return new android.os.IMessenger.Stub.Proxy(obj);
    }

就这样客户端就拿到了服务端的Messenger对象,接下来就可以在客户端中用Messenger给服务端发送消息了。

五. 小结

1.  客户端和服务端都有自己的Handler对象,用于在各自的进程中接收处理消息,发送消息则是和Handler绑定的Messenger对象。

2.  通过IMessenger.aidl 中的 send(Message msg)方法进行跨进程通信, 注意 msg.replyTo变量实则是一个Messenger对象, 用于两个进程传递各自的Messenger对象。

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

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

相关文章

Golang - 从源码到二进制:探索在国产CPU架构上交叉编译Minio的方法

文章目录 前置知识交叉编译Go 支持的所有操作系统和体系结构组合列出 Go 支持的所有操作系统和体系结构组合 大端、小端minio使用的go版本ABI 官方下载目标编译loongarch架构下的minio编译mipsle架构下的minio编译sw64架构下的minio 前置知识 交叉编译 交叉编译是指在一台主机…

ComfyUI 基础教程(十二):AI换脸神器 InstantID ComfyUI, 保姆级安装与使用测评

AI换脸技术发展迅速,roop,Reactor,ipadapter-faceid,photomaker,InstantID,换脸神器更新非常快。 对比lora训练,faceID,intantID,IPA,在人脸风格的迁移上,是目前AI换脸的主要方式。而最新出的InstantID,只需单个图像即可实现ID保留生成,相似度极高。 instantid特…

springboot项目之java.lang.NullPointerException: null问题

没有任何提示&#xff0c;只有一行报错: RuntimeException-[java.lang.NullPointerException] 问题定位: 最后发现是 controller文件&#xff0c;service定义一行少加了关键字 final 导致的&#xff0c; 补充之后就完美解决啦&#xff01; 参考 springboot项目之java.lang.Nu…

基于vue的个性化推荐餐饮系统Springboot

项目&#xff1a;基于vue的个性化推荐餐饮系统Springboot 摘要 现代信息化社会下的数据管理对活动的重要性越来越为明显&#xff0c;人们出门可以通过网络进行交流、信息咨询、查询等操作。网络化生活对人们通过网上购物也有了非常大的考验&#xff0c;通过网上进行点餐的人也…

软件实例分享,饭店餐饮会员卡管理系统怎么弄会员充值怎么记账

软件实例分享&#xff0c;饭店餐饮会员卡管理系统怎么弄会员充值怎么记账 一、前言 以下软件教程以 佳易王餐饮会员管理系统软件V16为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、会员可以登记电子会员卡或使用vip卡片 2、卡类型可以自由…

SICTF Round#3 Web方向 题解WP

100&#xff05;_upload 题目描述&#xff1a;小茂夫说&#xff1a;一直上传恶意文件尊嘟要生气了&#xff0c;世事莫固守&#xff0c;转变思路求突破 开题&#xff0c;注意有个文件包含 题目把后缀过滤死了&#xff0c;无法上传php后缀文件。文件内容些许过滤&#xff0c;短…

微信小程序uniapp校园在线报修系统维修系统java+python+nodejs+php

管理员的主要功能有&#xff1a; 1.管理员输入账户登陆后台 2.个人中心&#xff1a;管理员修改密码和账户信息 3.用户管理&#xff1a;对注册的用户信息进行删除&#xff0c;查询&#xff0c;添加&#xff0c;修改 4.维修工管理&#xff1a;对维修工信息进行添加&#xff0c;修…

【递归】【后续遍历】【迭代】【队列】Leetcode 101 对称二叉树 100. 相同的树

【递归】【后续遍历】Leetcode 101 对称二叉树 100 相同的树 101. 对称二叉树解法一&#xff1a; 递归&#xff1a;后序遍历 左右中解法二&#xff1a; 迭代法&#xff0c;用了单端队列 100. 相同的树解法一&#xff1a;深度优先 ---------------&#x1f388;&#x1f388;对称…

关于Windows 11中的Microsoft Defender,看这篇就差不多了

Windows内置的安全应用程序Microsoft Defender可保护你的计算机免受恶意代理和病毒的攻击。然而,在某些情况下,你可能想要禁用它,例如在测试第三方安全应用程序时。我们将向你展示如何在Windows 11上永久禁用Microsoft Defender。 何时应永久禁用Microsoft Defender防病毒 …

vue3前端excel导出;组件表格,自定义表格导出;Vue3 + xlsx + xlsx-style

当画面有自定义的表格或者样式过于复杂的表格时&#xff0c;导出功能可以由前端实现 1. 使用的插件 &#xff1a; sheet.js-xlsx 文档地址&#xff1a;https://docs.sheetjs.com/ 中文地址&#xff1a;https://geekdaxue.co/read/SheetJS-docs-zh/README.md xlsx-style&#…

【8】证书公钥替换

0x01 问题描述 存在一个前置系统&#xff0c;数据包有登录信息。登录需要填入用户名&#xff0c;证书上传&#xff0c;私钥。如图&#xff1a; 提供数据包如下&#xff1a; POST /api/certLogin HTTP/1.1 Host: 192.168.11.153 Connection: keep-alive Content-Length: 934…

srs集群下行edge处理逻辑

官方关于源站集群的介绍&#xff1a; Origin Cluster | SRS 下行边缘是指观众端从边缘edge拉流&#xff0c;边缘edge回源到源站origin节点拉流&#xff0c;然后再 把流转给客户端 边缘处理类SrsPlayEdge 当服务器收到播放请求时&#xff0c;创建对应的consumer消费者。在创…

【保真】揭秘目前唯一能使用Sora的官方渠道 —— OpenAI Red Teaming Network

原文链接&#xff1a;【保真】揭秘目前唯一能使用Sora的官方渠道 —— OpenAI Red Teaming Network 前几天OpenAI推出的Sora模型着实太火了&#xff0c;不仅让圈内人热血封腾&#xff0c;也给圈外人点了一把AGI的热情之火。 Sora的大火&#xff0c;也有不少小伙伴开始问一个问…

2.网络游戏逆向分析与漏洞攻防-游戏启动流程漏洞-项目搭建

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;1.网络游戏逆向分析与漏洞攻防-游戏启动流程漏洞-测试需求与需求拆解-CSDN博客 代码以提交到码云&#xff1a;https://gitee.com/dye_your_fingers/titan 开始搭建环境 创建之前&#xff1a;HOOK引擎…

集成厂家服务的核心内容

一、关系定义 集成商是指将多个不同的产品或服务集成在一起&#xff0c;形成一个整体解决方案&#xff0c;满足客户需求的企业或个人。 厂家产品商是指生产和销售产品的企业或个人&#xff0c;他们制造和提供各种产品&#xff0c;供集成商使用。 客户关系是指集成商和厂家产…

爬虫在网页抓取的过程中可能会遇到哪些问题?

在网页抓取&#xff08;爬虫&#xff09;过程中&#xff0c;开发者可能会遇到多种问题&#xff0c;以下是一些常见问题及其解决方案&#xff1a; 1. IP封锁&#xff1a; 问题&#xff1a;封IP是最常见的问题&#xff0c;抓取的目标网站会识别并封锁频繁请求的IP地址。 解决方案…

git push 使用 --mirror 参数复制仓库

迁移一个 Git 仓库并且保留原有的提交记录和分支 克隆原始仓库到本地 git clone <原始仓库URL> <新仓库目录>添加新的远程仓库&#xff1a;git remote add new-origin <新仓库URL>推送所有分支和标签到新的远程仓库&#xff1a;git push new-origin --mirro…

Vue封装全局公共方法

有的时候,我们需要在多个组件里调用一个公共方法,这样我们就能将这个方法封装成全局的公共方法。 我们先在src下的assets里新建一个js文件夹,然后建一个common.js的文件,如下图所示: 然后在common.js里写我们的公共方法,比如这里我们写了一个testLink的方法,然后在main…

计算机设计大赛 深度学习动物识别 - 卷积神经网络 机器视觉 图像识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

微信小程序uniapp校园租房指南房屋租赁系统java+python+nodejs+php

语言&#xff1a;javapythonnodejsphp均支持 框架支持:Ssm/django/flask/thinkphp/springboot/express均支持 运行软件:idea/eclipse/vscode/pycharm/wamp均支持 数据库 mysql 数据库工具&#xff1a;Navicat等 前端开发:vue 小程序端运行软件 微信开发者工具/hbuiderx uni-…