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

news2025/2/26 5:05:15

一. 概述

        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.coloradmin.cn/o/1460318.html

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

相关文章

51_蓝桥杯_独立按键

一 电路 注意&#xff1a;J5跳帽接到2~3引脚&#xff0c;使按键S4-S5四个按键的另外一端接地&#xff0c;从而成为4个独立按键。 二 独立按键工作原理 三 代码 代码1&#xff1a;按下S7点亮L1指示灯&#xff0c;松开按键&#xff0c;指示灯熄灭&#xff0c;按下S6点亮L2指示灯…

Spring Boot项目怎么对System.setProperty(key, value)设置的属性进行读取加解密

一、前言 之前我写过一篇文章使用SM4国密加密算法对Spring Boot项目数据库连接信息以及yaml文件配置属性进行加密配置&#xff08;读取时自动解密&#xff09;&#xff0c;对Spring Boot项目的属性读取时进行加解密&#xff0c;但是没有说明对System.setProperty(key, value)设…

养生系列文章目录 - 心学和冥想融合实践

养生系列文章目录 文章目录 养生系列文章目录前言一、冥想方式二、早起床上冥想三、喝水冥想四、走路冥想四、坐地铁冥想五、写字冥想六、沟通冥想七、学习冥想八、听音乐冥想九、工作冥想十、跑步冥想十一、睡前冥想总结 前言 王阳明&#xff08;1472-1529&#xff09;是中国明…

善于利用GPT确实可以解决许多难题

当我设计一个导出Word文档的功能时&#xff0c;我面临了一个挑战。在技术选型时&#xff0c;我选择了poi-tl这个模板引擎&#xff0c;因为在网上看到了很多关于它的推荐。poi-tl可以根据模板快速导出Word文档。虽然之前没有做过类似的功能&#xff0c;而且项目中也没有用过&…

STM32F10X(Cortex-M3)系统定时器寄存器笔记和系统定时器精准延时函数

Cortex-M3系统定时器寄存器笔记和系统定时器精准延时函数 简介系统定时器寄存器STK_CTRLSTK_LOADSTK_VALSTK_CALIB STM32F10X(Cortex-M3)精准延时函数 简介 在STM32F10X(Cortex-M3)除了通用定时器和看门狗定时器外&#xff0c;还有一个系统定时器(SysTick) 拿STM32F103C8T6来说…

SQL注入:网鼎杯2018-unfinish

目录 使用dirmap扫描 使用dirsearch扫描 使用acunetix扫描 爆破后端过滤的字符 绕过限制获取数据 这次的进行SQL注入的靶机是&#xff1a;BUUCTF在线评测 进入到主页面后发现是可以进行登录的&#xff0c;那么我们作为一个安全人员&#xff0c;那肯定不会按照常规的方式来…

企业必备!助你有效防止员工私删客户微信?

在企业管理中&#xff0c;保护客户资源和数据安全至关重要&#xff0c;特别是在微信这样的沟通工具中。为了有效防止员工私删客户微信以及滥用工作微信&#xff0c;企业可以借助一些专业的工具——微信管理系统来进行监控和管理。 首先&#xff0c;企业可以在微信管理系统上给…

CSS基础属性

【三】基础属性 【1】高度和宽度 &#xff08;1&#xff09;参数 width&#xff08;宽度&#xff09;&#xff1a;用于设置元素的宽度。可以使用具体的数值&#xff08;如像素值&#xff09;或百分比来指定宽度。 height&#xff08;高度&#xff09;&#xff1a;用于设置元…

【JGit】分支管理实践

本文紧接【JGit】简述及学习资料整理。 以下梳理了使用 JGit 进行 Git 操作的实践 JGit实践 主函数 public static void main(String[] args) throws Exception {String localDir "D:\\tmp\\git-test\\";String gitUrl "http://192.168.181.1:3000/root/g…

如何合理规划PCB叠层

目录 引言 6层板叠层设计方案 8层板叠层设计方案 10层板叠层设计方案 12层板叠层设计方案 总结 引言 PCB叠层是决定电子产品EMC性能的关键因素,合理的叠层布局,可以使得PCB上的差模和共模辐射最小化,反之,则可能放大这些辐射的干扰。 通常会从以下的因素中,对PCB的叠…

图扑数字孪生“光储充”一体化智慧充电站

近年来&#xff0c;蔚来、理想、特斯拉等电动汽车凭借独特的优势已成为全球消费者的“新宠儿”。随着新能源车保有量迅速增长&#xff0c;充电需求不断上升&#xff0c;充电桩对区域电网的冲击也日益显著。 “光储充”一体化模式&#xff0c;即“光伏储能汽车充电”&#xff0…

关于Windows 10中剪贴板的知识,看这篇文章就差不多了

本文介绍了如何在Windows10中使用剪贴板。除了有关复制、粘贴和清除剪贴板的信息外&#xff0c;还包括有关将项目固定到剪贴板的信息。 如何将内容复制到Windows 10剪贴板 Windows 10操作系统中的剪贴板比以前的剪贴板体验更先进。使用新的剪贴板&#xff0c;你可以查看复制到…

fastApi笔记04-查询参数和字符串校验

额外校验 使用Query可以对查询参数添加校验 from typing import Unionfrom fastapi import FastAPI, Queryapp FastAPI()app.get("/items/") async def read_items(q: Union[str, None] Query(defaultNone, max_length50)):results {"items": [{"…

【力扣hot100】刷题笔记Day8

前言 到了大章节【链表】了&#xff0c;争取两三天给它搞定&#xff01;&#xff01; 160. 相交链表 - 力扣&#xff08;LeetCode&#xff09;】 双指针 参考题解&#xff0c;相比于求长度右对齐再一起出发的方法简洁多了 class Solution:def getIntersectionNode(self, head…

zabbix5.0利用percona监控MySQL

具体来说包括: Percona Monitoring Plugins 这是一组用于收集MySQL实例各种性能指标和状态的插件脚本,包括: mysqld_stats.pl - 收集服务器状态计数器mysqld_statement_replay.pl - 进行负载模拟测试pt-status - 收集InnoDB资源使用情况等 Percona Templates 基于这些插件收集…

gitlab 项目上线,项目上线后回滚

gitlab 项目上线&#xff0c;项目上线后回滚 1.需要自己有个gitlab项目环境&#xff0c;没有找我&#xff0c;docker-compose 一键环境启动 2.发起合并请求3.选择合并的分支4.点击创建合并&#xff0c;然后确认合并合并完成&#xff0c;进行回滚操作&#xff0c;在合并详情页…

wo-gradient-card是一款采用uniapp实现的透明辉光动画卡片

采用uniapp-vue3实现&#xff0c;透明辉光动画卡片&#xff0c;卡片内容包含标签、标题、副标题、图片 支持H5、微信小程序&#xff08;其他小程序未测试过&#xff0c;可自行尝试&#xff09; 可用于参考学习 可到插件市场下载尝试&#xff1a; https://ext.dcloud.net.cn/plu…

仿12306校招项目-前后端运行

目录 1.git 克隆 2.设置JDK版本 3.sql脚本导入数据 4.启动中间件 5.运行后端 6.运行前端 1.git 克隆 打开 IntelliJ IDEA&#xff0c;菜单栏顶部找到 Git -> Clone 选项。找到 Clone 这个按钮输入 gitgitee.com:nageoffer/12306.git或者https://gitee.com/nageoffer/…

什么是企业数字化转型的关键要素?

企业数字化转型的关键要素通常包括以下几个方面&#xff1a; 战略规划&#xff1a;企业需要制定清晰的数字化转型战略&#xff0c;明确转型的目标、路径和时间表&#xff0c;确保数字化转型与企业整体战略相一致&#xff0c;为企业提供明确的指导。 数据驱动&#xff1a;数据是…

Android 7.0以上charles无法抓取部分https包问题

首先保证配置一切正确 手机通过访问chls.pro/ssl下载.pem证书&#xff0c;如无法安装&#xff0c;在文件管理器中将后缀名改为.crt 在设置中安装该证书 Charles-Proxy - SSL Proxying Setting - Include 添加需要抓包的URL:443即可 以上基本配置结束后&#xff0c;看下代码 代…