深入了解 Messenger 的实现细节

近一个半月因为工作变动的缘故,忙着交接工作和复习面试。没有多少时间来写博客,连一周三次的健身都有几个星期没练了,好多同事问我是胖了还是壮了(我迅速就岔开话题了,机智boy)。上周离职,这周主要在处理一些私事、做些入职准备工作、看点书之类的,下周入职YY(上周才知道原来大神罗升阳也在YY)。好啦,说了这么多,要开始进入 Messenger 的正题了。

前言

看这篇文章前,需要对 Android 的进程间通讯方式有所了解,不然可能会云里雾里。

从使用 Messenger 说起

Android 上实现 IPC (进程间通讯)的方式有好几种,其中有一种就是使用 AIDL 方式实现,对使用 AIDL 不了解的童鞋可以看下方的官方文档(需要梯子)。

https://developer.android.com/guide/components/aidl.html

对于使用 AIDL 方式通讯,其关键就在于创建 aidl 文件,系统会自动为 aidl 文件生成相应的 Java 类,其关键实现在于生成的 Java 类中。

系统提供了一个更方便我们进行 IPC 的类 —— Messenger,先来看看如何使用 Messenger(熟悉的童鞋完全可以跳过这一部分)。

  • 第一步:客户端进程创建两个 Messenger,一个 Sender ,一个 Receiver;

    //客户端进程发消息给服务进程
    private Messenger mSender;
    //客户端进程接收服务进程回调
    private Messenger mReceiver = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Bundle data = msg.getData();
            if (data != null) {
                String response = data.getString("body");
                Toast.makeText(MainActivity.this, response, Toast.LENGTH_SHORT).show();
            }
        }
    });
  • 第二步:编写Service类,并需要在 AndroidManifest.xml 配置多进程;

public class IPCService extends Service {

    private Messenger messenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            try {
                Thread.sleep(2 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message response = Message.obtain();
            Bundle data = new Bundle();
            data.putString("body", "response");
            response.setData(data);
            try {
                msg.replyTo.send(response);//回调客户端
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    });

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();//将Binder返回给请求绑定的进程
    }

}
  • 第三步:绑定Service进程,并在 ServiceConnection 中初始化 Sender;

    Intent intent = new Intent(this, IPCService.class);
    startService(intent);
    bindService(intent, conn, Context.BIND_AUTO_CREATE);//启动绑定Service进程

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mSender = new Messenger(service); //用Binder初始化Sender
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
  • 第四步:用 Sender 和 Receiver 完成客户端进程和服务进程的交互。

    if (mSender != null) {
        Message message = Message.obtain();
        message.replyTo = mReceiver;//将 Receiver 一并发送给服务进程
        try {
             mSender.send(message);
        } catch (RemoteException e) {
             e.printStackTrace();
        }
     }

至此,在绑定服务进程初始化 Sender 后,即可以做多进程间的交互工作了。使用 Messenger 来实现多进程的交互相比我们用 aidl 来要方便得多,但是 Messenger 的内部也是采用 aidl 实现的,只不过为了方便开发者调用而进行一些封装,使得开发者们可以忽略 aidl 的实现细节。简单的了解了 Messenger 的基本使用后,下面我们就来看看 Messenger 的源代码,了解一些内部的实现细节。

Messenger 源代码通读

Messenger 类位于 android.os 包下,代码量不是很多,所以看起来难度不大,只有如下这么几个方法。

在上面的示例代码中可以看到,客户端进程有 Sender 和 Receiver 两个 Messenger,如果不需要实现服务进程回调客户端进程,那么 Receiver 完全可以不要。当需要服务进程回调客户端进程时,则需要传入 Receiver 了。由于需要在进程间传递 Messenger 对象,那么 Messenger 类就必须要继承 Serializable 或者 Parcelable 接口。按照 Android 系统一向的风格,都是偏向于推荐继承 Parcelable 来实现。

所以,上面看到的 describeContents、writeToParcel 方法和 CREATOR 对象实际上继承 Parcelable 的实现。不明白的童鞋可以参照 Parcelable 的官方文档(需要梯子)

https://developer.android.com/reference/android/os/Parcelable.html

而 equals 和 hashCode 方法自然不用多说啦,大家熟悉得很。而 writeMessengerOrNullToParcel 和 readMessengerOrNullFromParcel 这对静态方法主要是实现 Messenger 在 Parcel 中的读写操作的,实现比较简单,大家参见下面代码就可以理解了。

当然,Messenger 除了继承 Parcelable 外,还需要声明一个同名的 Messenger.aidl 文件,可以在 framework 层源码下 android.os 包中找到 Messenger.aidl 文件,对于写过 aidl 的童鞋,肯定不陌生了。

排除掉上面提到的方法,剩下的主要是 Messenger 的两个构造方法以及 getBinder 和 send 方法。客户端调用服务进程方法时,是通过 Messenger 中的 send 方法,所以我们先直接看 send 方法中的内部实现

其内部是调用了 mTarget 的 send 方法,那么 mTarget 又是何物呢?

从上面可以看到 mTarget 是一个 IMessenger 实例,作为 Messenger 唯一的成员变量。 初始化 mTarget 是在 public Messenger(Handler target) 构造函数中,利用 Handler 的 getIMessenger 方法来获取一个 IMessenger 的实例。

从上图可以看到 getIMessenger 前面是没有修饰符的,这样控制了该方法的作用域仅限于 android.os 包内给其他类使用,我们日常开发是无法使用该方法的,所以在 API 文档中也没有提供相应接口文档。在倒数第二行中可以看到 new MessengerImpl() 并在最后 return 给调用者。所以实际上, mTarget 具体实现是在 MessengerImpl 中。

MessengerImpl 实际上是 Handler 的一个私有内部类,它继承了 IMessenger.Stub 并实现 send 方法。用过 aidl 的童鞋 IMessenger.Stub 的身影想必就明白了,实际上 IMessenger 就是系统提供的 IMessenger.aidl 文件,而 IMessenger.Stub 就是由 IMessenger.aidl 生成的类。 IMessenger.aidl 在 framework 层代码的 android.os 包中可以找到,而关于 IMessenger 的 Java 实现,则可以看下面的链接。

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/os/IMessenger.java/

MessengerImpl 的 send 实现相对也比较简单,只有两行代码


    public void send(Message msg) {
        msg.sendingUid = Binder.getCallingUid();
        Handler.this.sendMessage(msg);
    }

首先是将发起调用的客户端进程的 Linux Uid 存储在我们传入的 Message 对象中,服务进程收到 Message 可以通过 msg.sendingUid 得知发起调用的进程的 Linux Uid。接着通过 Handler 的 sendMessage 方法发送给服务进程,这意味着 Messenger 与服务进程间的操作是串行的,因此,在有并行需求的场景下 Messenger 就不适用了。

了解完了 send 方法后,最后就只剩下 getBinder 了,其内部实现也简单。

参照前面的示例代码,这里主要还是在 Service 的 onBind 方法中返回 Binder 对象给客户端调用,实现同样是 IMessenger 的 Java 实现中。

至此,基本上看完了整个 Messenger 的内部代码,从上面的分析上看,内部实现确实非常简单,基于 aidl 的基础上做的封装实现,又对开发者屏蔽了底层 aidl 的实现细节。当然个人认为有两点不足之处:

  • 一个前面提到的 send 操作串行,并行场景则无法用 Messenger;
  • Messenger 内部没有做 Binder 键断裂重连的处理(个人认为内部处理了会更好,更加屏蔽底层的实现细节);

远程调用的阻塞与非阻塞

Android 系统跨进程通讯的底层实现都是通过 Binder 实现,正常情况下客户端进程发起一个远程方法调用的流程大致如下:

1. 客户端线程发起调用;
2. 客户端线程将远程调用操作交给 Binder 线程池,并阻塞等待返回远程方法执行完毕返回;

这也就意味着,我们不能在客户端进程的 UI 线程中发起远程方法调用,不然如果远程方法执行了耗时操作,客户端的 UI 线程将会被阻塞,从而造成 ANR 的问题存在。读者可以自行尝试自定义 aidl 并发起一个耗时的远程方法调用进行验证。但是,如果你使用系统提供的 Messenger ,则不会出现这样的问题,无论你的远程方法执行多么耗时,客户端 Messenger 发起调用后会继续执行接下来的代码,并不会进行阻塞等待。这里让我百思不得其解,为什么呢?前面我们可以看到 Messenger 的 send 方法实现是在 MessengerImpl 中

并且,发送 Messeage 的操作是利用主线程的 Handler ,并没有其他的异步操作,为何执行的过程中不阻塞?这点我也完全没有想明白,最后折腾半天无果向任玉刚老师求助才得到答案。原来,默认情况下发起的远程方法调用都是阻塞式的,但也可以是非阻塞式的。 Messenger 就是采用非阻塞的方式通讯,其关键就在于 IMessenger.aidl 的实现

相比平常自定义的 aidl,多了 oneway 的关键字,声明和不声明 oneway 关键字的在于生成 Java 类中一个参数

不声明 oneway 时,mRemote.transact 传入的最后一个参数是 0;声明 oneway 时,mRemote.transact 传入的最后一个参数是 android.os.IBinder.FLAG_ONEWAY 。

查看 API 文档即可以看到 FLAG_ONEWAY 的作用就是让客户端能够非阻塞的调用远程方法,至此真相大白,如果我们自定义的 aidl 也想实现非阻塞的调用,只需声明 oneway 关键字即可。

总结

因为平时并不常用到 oneway,加上文档提及的很少,唯一有描述的就是下面这段。这次看了 Messenger 的代码才知道有这么回事,也是涨姿势了。

对我的博文感兴趣,可以关注我的简书哟:

http://www.jianshu.com/users/ec95b5891948/latest_articles

Flask+ngrok实现Facebook Messenger机器人公网部署 Webhook是连接聊天机器人与外部消息平台的核心通信机制,其本质是服务端暴露的HTTPS回调端点,遵循标准HTTP协议与签名验证原理。技术价值在于以轻量级方式实现双向实时消息路由,规避复杂网关与中间件。典型应用场景包括客服自动化、营销触达和SaaS工具集成。本文聚焦于使用Flask构建可验证Webhook服务,并借助ngrok快速获取可信HTTPS隧道,完成Facebook Messenger平台接入——涵盖Token验证、消息收发、签名校验及常见故障排查,是Python开发者落地Bot连接层的关键实践。 阅读详情

相关推荐

MessengerJS-master(iframe跨域通信)

MessengerJS-master(iframe跨域通信) MessengerJS-master(iframe跨域通信)

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

Android中比较有代表性的两大通信机制:1. 线程间Handler通信 2. 进程间Binder通信,本篇文章中我们在理解AIDL原理的基础上来解读一下Messenger的源代码, 并结合示例Demo加深理解。在看本篇文章前,建议先查阅一下笔者的Android 进程间通信机制(六) 手写AIDL文件首先说下我对Messenger的个人理解:1. 从概念上阐述Messenger进程间通信的信使,是一个轻量级的IPC通信方案, 和Message消息不是一个概念。2. 从实现上描述。

u012514113的专栏 1870

allegro转AD-(附工具)

本文介绍了将Allegro的.brd文件转换为Altium Designer可用的.alg文件的操作流程。首先需要下载专用转换工具,然后通过运行.bat脚本将.brd文件转换为.alg格式。最后在Altium Designer中使用导入向导完成文件转换,生成可用的PCB设计文件。整个过程简单快捷,只需三个步骤即可实现Allegro到Altium Designer的工程文件转换。

weixin_50120208的博客 1717

Android进程间通信(一):Messenger

Messeger适用于跨进程通信时不需多线程交互的情况。如若需要跨进程多线程交互,可以使用AIDL(请看《Android进程间通信(二):AIDL》)。一、可运行代码类(Runnable)public interface Runnable { /** * 把需要运行的代码放在这个方法中 */ public void run(); }二、线程类(Thread类)p

Wesnoth的专栏 870

OpenCV卡尺工具实战:5步搞定工业级圆与直线拟合(附完整C++源码)

本文详细介绍了使用OpenCV卡尺测量工具实现工业级圆与直线拟合的5个关键步骤,包括核心原理、环境配置、边缘点提取优化、鲁棒性几何拟合实现及QT集成技巧。通过模块化C++源码和实战案例,帮助开发者快速掌握高精度尺寸测量技术,适用于机械零件、PCB板等工业检测场景。

uran的专栏 843

ios快捷指令进行钉钉打卡

首先 1、打开快捷指令 2、自动化 3、创建个人自动化-特定时间 4、添加操作-搜索“随机数” 5、最小值和最大值填充 6、添加操作-搜索“等待” 7、长按“1秒钟”,选择魔法变量,选择上一步的随机数 6、添加操作-搜索“钉钉” 7、选择钉钉-随便一个聊天窗口。 8、添加操作-搜索“等待”,这次将等待时间设为10秒 9、添加操作 “搜索天气” ,选择显示天气。 注意点: 1、此操作依赖于钉钉极速打卡,只是帮忙唤起钉钉。 2、保存自动化时有个运行前询问,务必关掉。 3、手机不能设

blaze_jack 5万+

Android 基于Message的进程间通信 Messenger完全解析

转载请标明出处: http://blog.youkuaiyun.com/lmj623565791/article/details/47017485; 本文出自:【张鸿洋的博客】 一、概述 说到Android进程间通信,大家肯定能想到的是编写aidl文件,然后通过aapt生成的类方便的完成服务端,以及客户端代码的编写。如果你对这个过程不熟悉,可以查看Android a...

weixin_30492047的博客 764

最全卷积总结篇(分组、深度可分离、空洞、转置等)

分组卷积(Group Convolution)是一种卷积操作方式,它最早在AlexNet中引入,并在深度学习中被广泛使用,尤其是在高效模型设计(如MobileNet和ResNeXt)中。分组卷积通过将输入特征通道分为若干组,并在每组内单独进行卷积操作,可以减少计算复杂度和参数量,同时有效利用模型的结构特性。分组卷积的工作原理通道分组:输入特征图的通道图为C_in,输出特征图的通道数为C_out,将每组的C_in和C_out分为G组,每组的通道数为C_in/G和C_out/G。组内卷积。

qq_44815135的博客 4219

Android 基于Message的进程间通信

实例在最后,先简析原理。 1、服务端重要代码解析 @Nullable @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } getBinder里面是这样的 public IBinder getBinder() { return mTarget.asBinder(...

Jeffray1991的博客 365

VS2103+Intel2013+MSMPI运行Fortran并行程序

VS2103+Intel2013+MSMPI运行Fortran并行程序MSMPI安装新建Fortran90项目编写Fortran并行程序在cmd运行编译完成的执行文件 MSMPI安装 我这里采用的是Microsoft MPI v9.0.1,可以点此下载。下载完成后同时安装msmpisdk.msi、msmpisetup.exe两个文件,安装位置默认即可。 其中msmpisdk.msi的默认安装路径在C:\Program Files (x86)\Microsoft SDKs\MPI,该文件包含了mpi环境所需

weixin_43176703的博客 2937

Android 10.0 任务栏音乐播放器后台播放杀进程功能实现

在10.0的系统rom定制化开发过程中,在某些情况下在安装第三方播放器以后,播放音乐的时候,在最近任务栏recents列表中,点击全部清除,发现音乐播放器还在播放音乐,导致出现bug,完整的 解决方法,肯定是需要点击全部清除以后,音乐播放器也被杀掉进程,接下来看下这个问题怎么解决

安卓兼职framework和app工程师的博客 652

Android 进程间通信中 Messager 的简单使用

Messenger是 Android 中一种简单且高效的进程间通信(IPC)机制。它基于Binder和Handler实现,适用于轻量级的跨进程通信场景。相比 AIDL(Android Interface Definition Language),Messenger更加简单易用,但功能相对有限。以下是MessengerMessengerHandlerMessengerHandlerServiceMessengerMessageBundle服务端需要创建一个Service,并在其中定义一个Handler。

loutengyuan的专栏 1291

视觉Transformer实战 | Transformer详解与实现

本文系统介绍了 Transformer 架构的核心原理与实现。相比传统 RNN 和 CNN,Transformer 凭借自注意力机制有效捕捉序列中的长距离依赖关系,支持并行训练,显著提升效率。其核心创新在于通过查询、键、值向量计算注意力权重,理解单词间的语义关联。架构包含编码器-解码器结构:编码器通过多头自注意力和前馈网络提取输入特征;解码器利用掩码自注意力和交叉注意力生成输出。本文使用 PyTorch 实现了缩放点积注意力、位置编码、层归一化等关键模块,并构建了完整的 Transformer 模型。

盼小辉丶的博客 4万+

Messenger 消息工具菜单:回复、转发、删除 3 大常用操作的实现与 UI 细节

本文带你拆解 iOS 实时消息应用 Messenger(mChat)中的消息工具菜单:长按任意一条消息,即可唤出 **回复(Reply)、转发(Forward)、删除(Delete)** 三大常用操作。我们将围绕这套消息菜单的交互触发、UI 细节与底层实现,帮助新手理解一个即时聊天 App 是如何优雅地完成消息管理的。 ## 一键唤出:长按消息工具菜单的交互设计 在 Messenger 的聊天

gitblog_01024的博客 741

基于Matlab的无迹卡尔曼滤波惯性导航与DVL组合导航

航天航空领域中的惯性导航是一种基于惯性测量单元(Inertial Measurement Unit,IMU)的导航方法,可以提供航向、速度和位置等信息。深度/速度测量器(Doppler Velocity Log,DVL)是一种常用的传感器,可用于测量水下航行器的速度。通过将DVL与无迹卡尔曼滤波相结合,可以实现高精度的组合导航。本文介绍了基于Matlab的无迹卡尔曼滤波(UKF)惯性导航与DVL组合导航的方法。以上是一种简化的示例代码,您可以根据实际应用需求进行相应的修改和优化。

DevSavantX的博客 418

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

如何用Symfony Messenger实现异步任务处理?一线架构师实战分享

掌握异步任务处理核心技巧,本文基于PHP Symfony开发指南,详解如何用Symfony Messenger实现消息队列、延迟任务与系统解耦。涵盖常见应用场景、配置步骤及性能优化策略,提升应用响应效率,一线架构师实战经验值得收藏。

InstrFun的博客 1037

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Messenger

Messenger Messenger是一种轻量级的IPC方案,底层实现是AIDL。它对AIDL进行了封装,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,因为不存在并发执行的情形。 服务端进程:需要创建一个Service处理服务端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。 客户端进程:首先需要绑定服务端的Service,绑定成功后用服务端返回的I

时代我西的博客 345

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

详解 C# 中基于发布-订阅模式的 Messenger 消息传递机制:Messenger.Default.Send/Register

文章摘要: 本文详细解析了 C# 中基于发布-订阅模式的 Messenger 消息传递机制,重点介绍了 Messenger.Default.Send 和 Messenger.Default.Register 的使用方法及其在 MVVM 架构中的应用。Messenger 通过松耦合的方式实现跨组件通信,支持消息的发送与订阅,并通过令牌机制实现定向消息传递。文章结合历史对话中的代码示例,展示了如何通过 Messenger 发送和接收消息.

编程技术探索者,分享C/C++、C#、Java、数据库等开发经验,聚焦实战技巧与AI兴趣,助力编程爱好者成长。 2453

探索未来交互:基于Go语言的Facebook Messenger机器人开发库 —— Messenger

探索未来交互:基于Go语言的Facebook Messenger机器人开发库 —— Messenger 在当前这个数字化时代,人机交互正在以前所未有的速度进化。Facebook Messenger作为全球最受欢迎的消息平台之一,提供了无限的可能性给开发者,以创新的方式与用户连接。今天,我们将深入探讨一款名为 Messenger 的开源项目,一个专为构建Facebook Messenger机器人而设...

gitblog_00583的博客 804

MVVM中的Messenger

在开发Wpf/SL应用时,经常会遇到不同页面和窗体之间的参数传递的问题。对于这类问题,我们一般通过事件实现数据传递,也可以定义全局静态变量来进行数据共享。这里我们则使用了另外一种非常高效而优雅的方法来进行消息传递,这里我称之为Messenger,事实上,Messenger并非mvvm的专利,我们可以把它看作一种设计模式,你可以在其它.net程序中使用它。   一、Mvvm Light Mess

yulongguiziyao的专栏 4813

Python部署Facebook Messenger Bot生产实践

Messenger Bot本质是事件驱动的反向代理服务,需满足低延迟响应、高并发连接与无状态伸缩三大工程要求。其核心原理在于Webhook回调机制与Facebook Graph API的严格时序约束,技术价值体现在7×24稳定承载真实业务流量、毫秒级故障定位与跨行业可复用架构。典型应用场景包括电商订单查询、私教预约调度与AI口语陪练等对话式服务。本文聚焦Flask+Gevent轻量协程方案,深入解析签名验证、消息分发三级架构、ECS容器化部署及request_id全链路日志追踪等硬核落地细节,直击Deplo

392

Zenstruck Messenger Monitor Bundle 安装与配置指南

Zenstruck Messenger Monitor Bundle 安装与配置指南 1. 项目目录结构及介绍 Zenstruck's Messenger Monitor Bundle 是一个用于监控Symfony应用中Messenger组件的扩展,它提供了直观的界面来追踪消息工作者、传输、计划任务和消息状态。以下是该仓库的基本目录结构概览及其重要组成部分: .gitattributes, ....

gitblog_00597的博客 762

kickstart.nvim 完整上手指南:从零搭建个人 Neovim 配置的启动点

kickstart.nvim 是一份面向个人 Neovim 配置的"启动点"(launch point)项目:它刻意保持小巧、以单个 `init.lua` 文件为核心、并且逐行注释完整,目标是让你能读懂配置的每一行、按需修改,进而搭建出属于自己的编辑器。本指南将以仓库 [README.md](https://link.gitcode.com/i/57967cd59b6836654e8a725d3b

gitblog_00516的博客 890

【Android】跨进程通信AIDL和messenger详解

1.定义 我们都知道,Android应用一旦启动就会生成一个以包名为名字的进程,当然我们还知道android中很多manager都是运行在system server进程中的,像AMS,PMS,WMS等,它们都是通过binder来进程远程调用,说到这就不得不说多进程之间的通信问题,进程不像线程那样,多线程可以共享内存,而每个进程的内存都是独立的,无法直接访问,因此安卓提供了binder来进行进程间的...

风吹起如花般细碎流年 751

开源项目推荐:基于JavaScript的Wat Talk Messenger Bot

开源项目推荐:基于JavaScript的Wat Talk Messenger Bot 1. 项目基础介绍 本项目是基于Gary Bernhardt的Wat Talk的一个开源项目,它是一个利用JavaScript编写的messenger机器人。该项目可以在GitHub上找到,项目名称为bot-wat。此项目遵循MIT开源协议,允许用户自由使用、修改和分享。 2. 核心功能 该项目的核心功能是作为一...

gitblog_01103的博客 441
上一篇: ScratchView:一步步打造万能的 Android 刮奖效果控件
下一篇: Android应用瘦身,从18MB到12.5MB
D_clock爱吃葱花
博客等级 码龄14年 163粉丝 13原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值