带你理解 Android 消息机制原理

TEC控制器芯片 ADN8835 中文数据手册 ADN8835是一款高集成度的单芯片TEC控制器。它包括线性电压调节模块脉冲宽度调制(PWM) 电压调节模块和两个零漂移、轨到轨斩波放大器。线性控制器和PWM驱动器用于控制H桥配置中的内部功率MOSFET。通过测量热传感器反馈电压,并使用集成的运算放大器作为比例-积分-微分(PID)补偿器来调理信号,ADN8835通过TEC驱动电流,将连接至TEC模块的激光二极管或无源组件的温度稳定到编程的目标温度。 阅读详情

Android的消息传递,是系统的核心功能,对于如何使用相信大家都已经相当熟悉了,这里简单提一句。我们可以粗糙的认为消息机制中关键的几个类的功能如下:
Handler:消息处理者
Looper:消息调度者
MessageQueue:存放消息的地方
使用过程:
Looper.prepare > #$%^^& > Looper.loop(死循环) — loop到一个消息 > Handler处理
好了,我们直接看源码吧。
Java层
消息机制是伴随线程的,也就是说上面的几个类在可以在任何一个线程中都有实例的。
先看Looper吧。以主线程为例,Android进程在初始化,会调用prepareMainLooper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
…
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
…
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
以上几个方法就是Looper初始化,如果是主线程Looper会创建一个不可退出的MessageQueue,并把looper实例放入线程独立(ThreadLocal)变量中。
Looper#loop
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;? {
Message msg = queue.next();
if (msg == null) {
return;
}
…
try {
msg.target.dispatchMessage(msg);
}
…
msg.recycleUnchecked();
}
}
Looper prepare后就可以loop了,loop非常简单,一直去queue中拿消息就好了,拿到了交给target也就是Handler处理。大家有可能会奇怪这种死循环,执行起来不会太sb粗暴了吗?其实这个解决方式在queue.next!!!后面再讲。
Handler#dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handler收到后,如果发现message的callback不为空,则只处理callback。(提一句,我们用的很多的handler.post(Runnable),其实这个Runnable就是这里的callback,也就是说post的Runnable实质上是一个优先级很高的Message),如果没有则尝试交给handler本身的callback处理(handler初始化的时候可以用callback方式构造),再没有才到我们常用的handleMessage方法,这里就是我们经常重写的方法。
再说说消息的发送,一般handler会调用sendMessage方法,但是最终这个方法还是会跑到这里
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
再交给MessageQueue
boolean enqueueMessage(Message msg, long when) {
。。。
synchronized (this) {
。。。
Message prev;
for (;? {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
。。。
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
if (needWake){
nativeWake(mPtr);
}
}
return true;
}
MessageQueue会把消息插入队列,并依次改变队列中各个消息的指针。
咦,好像只用Java层貌似就能把整个消息机制说通了,native代码在哪儿?有何用呢?
但是,刚才提到了Looper初始化的时候也会新建一个MessageQueue
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
好了,我们第一个native方法出来了。这时候我们可以猜得到,MessageQueue才是整个消息机制的核心!
Native层
接上面Java层的代码,MessageQueue构造的时候会调一个nativeInit。
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
。。。
}
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
native层调用init方法后,会在native层构建一个native Looper!来看看native Looper的初始化
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
…
rebuildEpollLocked();
}
这里创建了一个eventfd,代码来自最新的8.0,这部分和5.0 pipe管道的mWakeReadPipeFd和mWakeWritePipeFd稍微有点不一样,前者是等待/响应,后者是读取/写入。只是android选取方式的不同而已,这块就不细说。
void Looper::rebuildEpollLocked() {
。。。
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, “Could not create epoll instance: %s”, strerror(errno));
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event));
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
。。。
}
再到rebuildEpollLocked这个方法中,可以看到通过epoll_create创建了一个epoll专用的文件描述符,EPOLL_SIZE_HINT表示mEpollFd上能监控的最大文件描述符数。最后调用epoll_ctl监控mWakeEventFd文件描述符的Epoll事件,即当mWakeEventFd中有内容可读时,就唤醒当前正在等待的线程.。
这里不了解的人可能听着晕,上面这么一大段一句话概括就是:Android native层用了Epoll模型。什么是Epoll模型呢?我先简单介绍一下。
Epoll(必看!!!)
为什么要引入呢?
在Looper.loop的时候提到了,android不会简单粗暴地真的执行啥都没干的死循环。刚才说了,问题出在queue.next。Epoll干的事就是: 如果你的queue中没有消息可执行了,好了你可以歇着了,等有消息的我再告诉你。这个queue.next就是“阻塞”(休眠)在这里。
Epoll简单介绍
1、传统的阻塞型I/O(一边写,一边读),一个线程只能处理一个一个IO流。
2、如果一个线程想要处理多个流,可以采用了非阻塞、轮询I/O方式,但是传统的非阻塞处理多个流的时候,会遍历所有流,但是如果所有流都没数据,就会白白浪费CPU。
。。。
于是出现了select和epoll两种常见的代理方式。
3、select就是那种无差别轮询的代理方式。epoll可以理解为Event poll,也就是说代理者会代理流的时候也伴随着事件,因此有了对应事件,就可以避免无差别轮询了。
4、其通常的操作有:epoll_create(创建一个epoll)、epoll_ctl(往epoll中增加/删除某一个流的某一个事件)、epoll_wait(在一定时间内等待事件的发生)
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
好了,我们结合Looper初始化的代码来读一下epoll在这里干了什么吧。
我直接翻译了:往mEpollFd代理中、注册、一个叫mWakeEventFd流、的数据流入事件(EPOLLIN)
这样大家应该懂了吧。。。
接上MessageQueue在初始化后,在native创建了一个Looper。
我们继续消息的发送和提取在native层的表现。其实native层主要负责的是消息的调度,比如说何时阻塞、何时唤醒线程,避免CPU浪费。
native发送
发送在native比较简单,handler发送消息后,会到MessageQueue的enqueueMessage,此时在线程阻塞的情况下,会调用nativeWake来唤起线程。
void NativeMessageQueue::wake() {
mLooper->wake();
}
void Looper::wake() {
。。。
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
。。。。
}
}
这里TEMP_FAILURE_RETRY是一个宏定义,顾名思义,就是不断地尝试往mWakeEventFd流里面写一个无用数据直到成功,以此来唤醒queue.next。这部分就不多说了。
native消息提取
也就是queue.next
Message next() {
。。。
for (;? {
。。。
nativePollOnce(ptr, nextPollTimeoutMillis);
。。。
}
}
可以看到,又是一个死循环(阻塞)。继续往下看
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
。。。
mLooper->pollOnce(timeoutMillis);
。。。
}
mLooper->pollOnce
mLooper->pollInner
int Looper::pollInner(int timeoutMillis) {
。。。
int result = POLL_WAKE;
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

mPolling = false;
mLock.lock();

for (int i = 0; i < eventCount; i++) {
    int fd = eventItems[i].data.fd;
    uint32_t epollEvents = eventItems[i].events;
    if (fd == mWakeEventFd) {
        if (epollEvents & EPOLLIN) {
            awoken();
        } else {
            ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
        }
    } 
    。。。
}
。。。
mLock.unlock();
。。。
return result;

}
在这里,我们注意到epoll_wait方法,这里会得到一段时间内(结合消息计算得来的)收到的事件个数,这里对于queue来说就是空闲(阻塞)状态。过了这个时间后,看看事件数,如果为0,则意味着超时。否则,遍历所有的事件,看看有没有mWakeEventFd,且是EPOLLIN事件的,有的话就真正唤醒线程、解除空闲状态。
消息机制在native层的主要表现就是这些。
想要安卓高级视频学习资料的加群:861531554

Windows 下 Codex MCP 服务配置全攻略:从环境搭建到实战避坑 本文详细介绍了在Windows系统下配置Codex MCP服务的完整流程与实战避坑指南。内容涵盖从Node.js环境搭建、Codex CLI安装,到核心配置文件config.toml的编写与解析,并重点解决了Windows平台特有的环境变量设置、MCP服务启动命令适配等关键难点,帮助开发者顺利完成从环境准备到编辑器插件连接的整个配置过程。 阅读详情

相关推荐

VCSA 6.7证书过期别慌!手把手教你通过SSH修改系统时间恢复登录(附STS证书修复脚本)

本文详细介绍了VCSA 6.7证书过期的应急处理方案,包括通过SSH修改系统时间临时恢复登录、全面更新证书体系以及专项修复STS证书的实战步骤。文章还提供了后期验证和系统加固的建议,帮助运维人员有效应对证书过期问题,确保虚拟化平台的稳定运行。

weixin_31292729的博客 415

深入理解 Android 消息机制原理

欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~作者:汪毅雄导语: 本文讲述的是Android的消息机制原理,从Java到Native代码进行了梳理,并结合其中使用到的Epoll模型予以介绍。Android的消息传递,是系统的核心功能,对于如何使用相信大家都已经相当熟悉了,这里简单提一句。我们可以粗糙的认为消息机制中关键的几个类的功能如下:Handler:消息处理者Looper:消息调度者...

weixin_34380296的博客 198

STM32与K210串口通信

这篇文章是为了填上一篇埋下的坑,k210和STM32通过串口通信,我是采用数据包的形式发送数据的,因为K210发送的数据有几种,采用数据包的形式发送比较安全。本文代码运行的平台是STM32F103C8T6和K210 Dock,只涉及到K210发送数据给STM32,stm32将接受到的数据显示到0.96寸OLED屏上。

qq_70715693的博客 3万+

队列(Queue)的实现及分析

1.队列的介绍: 1) 队列是一个有序列表,可以用数组或是链表来实现。 2) 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出。 3) 我们把向队列中插入元素的过程称为入队(Enqueue),删除元素的过程称为出队(Dequeue)并把允许入队的一端称为队尾,允许出的的一端称为队头,没有任何元素的队列则称为空队。其一般结构如下: 2.常用关于队列的操作: package com.zejian.structures.Queue; /** * Created by zeji

weixin_62226325的博客 3050

Docker版OnlyOffice中文字体配置全攻略:从字号调整到字体添加

本文详细介绍了在Docker版OnlyOffice中配置中文字体和调整字号的完整方案。通过容器内操作和配置文件修改,实现中文标准字号映射及思源、阿里巴巴等优质字体的集成,提升文档排版的规范性和专业性。特别针对企业级部署提供了性能优化建议和常见问题解决方案。

weixin_28309571的博客 136

Attention优化|2w字原理&图解: 从Online-Softmax到FlashAttention V1/V2/V3

本文通过原理分析和图解的方式,通俗易懂地FlashAttention系列算法。FlashAttention V1/V2在LLM领域的应用已经非常广泛,相关的论文也反复读了几遍。FA1和FA2论文非常经典,都推荐读一下(不过FA2论文中公式错误不少本文大约1.8w字,包括以下内容:0x07 分布式训推使用FlashAttention0x09 FlashAttention中MQA/GQA以及Causal Mask处理。

具身机器人和通用AI 5232

队列(Queue)的详解

双端队列是指允许两端都可以进行入队和出队的操作的队列,Deque是一个接口,使用时必须创建LinkedList的对象。底层还是一个数组,指定一个队列的大小,入队时,当队列满的时候就不能再放了,出队时当队列为空的时候就不能再出队了。队列,在Java中是用双向链表实现的,实现类是LinkedList,性质是先进先出 FIFO。队列:只允许在一端进行插入操作,在另一端进行删除数据操作的特殊的线性表,队列具有先进先出。注意:栈,在Java中是用数组实现的,Stack继承自Vector,性质是后进先出 LIFO。

m0_53677355的博客 3577

innovus:如何在init design之后新增参考库(lib/lef)

如题,在PR过程中经常会遇到这种问题,时序无法收敛,需要新增一套lvt或者hpc的库,怎么办?

m0_61544122的博客 5004

libuv源码分析之queue

libuv的queue实现得很博大精深。严重考验了c指针的理解。今天就分享一下他的实现。首先从一个typedef开始 typedef void *QUEUE[2]; 这个是c语言中定义类型别名的一种方式。比如我们定义一个变量 QUEUE q 就相当于 void *q[2]; 即一个数组,他每个元素是void型的指针。 下面我们接着分析四个举足轻重的宏定义,理解他们就相当于理解了libuv的...

标子 的专栏 566

uniapp [全端兼容] - 实现人脸识别对比+人脸相似度,从相册选择或拍照上传两张图片进行人脸对比得到人脸相似度及判断是否是同一个人,人脸活体检测及对比(免费纯前端方案+调用第三方API接口方案)

uniApp人脸对比 人脸对比识别 人脸对比相似度,uniapp实现获取手机摄像头权限,调用相机拍照实现人脸识别相似度对比,uniapp实现人脸识别对比,计算两张人脸的相似度,从而判断是否同一个人,并给出相似度评分。uniapp H5网页如何实现人脸相似度比对,uniapp纯前端怎么对比两张图片的相似度,计算两幅图像的相似度,图像相似度对比方法,UNI-APP人脸相似度对比,快速识别两张照片的相似程度(用百分比)图片相似度对比在线测试,图片相似度对比,uniapp微信小程序人脸相似度对比分析,uniapp

🏆 前端领域优质创作者 5020

ceph recovery 相关参数

可以修改的最大回填/恢复选项列在 “恢复/回填选项” 部分。mClock 的修改 默认回填/恢复限制由 osd_mclock_override_recovery_settings 选项,设置为 默认为 false。尝试修改任何默认恢复/回填限制而不设置门控选项,会将该选项重置为 mClock 默认值,并在集群日志中记录一条警告消息。请注意,默认值可能需要几秒钟才能恢复生效。请使用 config show 命令验证限制,如下所示。就直接设置以下参数,过一会就变回了默认值。简单点说官网的意思就是不设置。

weixin_40548182的博客 277

干货 | 45张图庖丁解牛18种Queue,你知道几种?

来源 |悟空聊架构(ID:PassJava666)在讲《21张图讲解集合的线程不安全》那一篇,我留了一个彩蛋,就是Queue(队列)还没有讲,这次我们重点来看看Java中的Queue家...

架构师小秘圈 584

华为OD机试真题 新系统【小花获胜的奶茶】

华为OD机试真题 新系统【小花获胜的奶茶】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【小花获胜的奶茶】多语言题解。

qq_45776114的博客 382

回转寿司你一定吃过!——Android消息机制(分发)

这是“Android消息机制”系列的第二篇文章,系列文章目录如下: 回转寿司你一定吃过!——Android消息机制(构造) 回转寿司你一定吃过!——Android消息机制(分发) 回转寿司你一定吃过!——Android消息机制(处理) 消息机制的故事 寿司陈放在寿司碟上,寿司碟按先后顺序被排成队列送上传送带。传送带被启动后,寿司挨个呈现到你面前,你有三种享用寿司的方法。 将Android概...

weixin_33936401的博客 144

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Android开发实践:自定义带消息循环(Looper)的工作线程

上一篇文章提到了Android系统的UI线程是一种带消息循环(Looper)机制的线程,同时Android也提供了封装有消息循环(Looper)的HandlerThread类,这种线程,可以绑定Handler()对象,并通过Handler的sendMessage()函数向线程发送消息,通过handleMessage()函数,处理线程接收到的消息。这么说比较抽象,那么,本文就利用基础的Java类库,...

weixin_34291004的博客 167

OV9734 DATASHEET,完整规格书

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

android 队列管理器,Android消息队列浅析

当面试官问到你消息对列的时候,恭喜你,已经跨过初级,在试探你的中级水平了。Android的消息循环是参考Windows的消息循环机制来实现的。消息队列4件套Message、MessageQueue、Looper、Handler1、Message是消息对列的消息实体类,因为消息队列中会存放最多10个Message对象。常用属性what,是消息体的Tag,用来区分是那个一消息体。2、Messa...

weixin_33431149的博客 353

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

android Handler Looper,MessageQueue消息机制原理

安卓消息处理类: Looper、Handler、MessageQueue、Message、ThreadLocal、ThreadLocal.Values、HandlerThread。 Looper: 线程默认是没有消息循环的,要为一个线程创建一个消息循环通过调用prepare(),然后在调用loop()方法进入消息循环(这意味着线程将一直在此方法循环)。例如: class LooperThr

ZhouLi_优快云的专栏 9992

android消息机制原理详解

android消息机制原理详解Android消息机制概述说到Android的消息机制,Android初级工程师(不包括那些初学者)肯定会想到Handler。是的,Android的消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程。当我们工作的时候我们只要接触到Handler就可以了。为什么要有Android消息机制?我们知道Handle

欧阳帆的专栏 9046

深入理解Android消息机制

前言 Android的消息机制主要说的是Handler的运行机制,相信大家对Handler已经非常熟悉了,Handler可以轻松的将一个任务切换到Handler所在的线程中去执行。最熟悉的就是我们只能在UI线程中更新UI,所以我们经常来用Handler来更新UI,但Handler并不是专门用来更新UI的。本文源码基于Android8.0。 一、为什么只能在主线程中访问UI 可...

黄林晴 5085

很容易理解的Android消息机制分析

最近经常使用Handler相关的一些东西。翻了下源码,总结一遍方便回顾。 Android消息机制就是Android的handler机制。 首先是为什么要使用handler? 子线程benign操作ui,因此需要将操作ui的消息传递到主线程,使主线程按照需求更新ui,避免操作不安全。 Handler的重要组成部分:Handler Looper MessageQueue ,其中Looper是Handler和messagequeue通信的桥梁。 Handler是消息处理器。MessageQueue.

xululu0401的博客 1684

带你看懂Android消息机制

前言 本篇文章我们继续以源码的角度分析Android的消息机制.我们知道在Android中经常需要在不同的线程中进行通信。当线程A中启动线程B,去执行一些数据操作时,若线程B执行数据操作所耗费的时间大于线程A时,就会使得A线程提前结束,那么B线程操作数据之后的结果将没有任何意义。所以便引出消息机制的概念。其原理是通过一个MessageQueue消息队列来存储其他线程发送的消息,通过本线程的Looper轮询器不断地取消息队列中的消息并通知给当前线程。接下来我们将进入消息机制源码层去探索它的奥秘。 关键知识及流

问彡心的博客 1105

对Android消息机制的理解

对Android消息机制的理解 Android的消息传递,是系统的核心功能 预备知识: 什么叫做消息? 消息(Message)代表一个行为(what)或者一串动作(Runnable),每一个消息在加入消息队列(MessageQueue)时,都有明确的目标(Handler) android怎么使用消息 // 子线程 private class WorkThread extends...

HAZU_ing的博客 300

Android消息机制原理全面讲解

一、简介 消息机制这个知识点,从我们第一天写android应用时就接触到了,可能大部分时间我们都用它来更新主线程的UI界面,使用上应该没事问题,但是知其然还要知其所以然,今天就让我们通过一篇文章彻底的,透彻的,永久的理解并记住Android消息机制的原理。整篇文章会围绕下面几个问题展开: 1、消息机制的作用? Handler作为消息机制的发起者和终结者,我们看看Handler源码中的注释是如何说的...

fsf_snail的专栏 1129

Android 消息机制原理解析

一、概序关于Android消息机制的使用,大家一个想到的就会是在子线程中更新控件,确实,安卓设计出这个机制很大一部分原因是处于这个目的,但是并不是全部。本篇文章,作者也会从源码的角度去分析消息机制的运作方式,希望大家能从文章中了解到消息机制的使用方法以及它为什么能被这样用。二、Android消息机制首先我们来看一下平常使用消息机制的方式:public class MainActivity exten

u010618194的博客 553

Android消息机制

综上,举个例子,再线程A中希望发送一个消息给到线程B,那需要做什么呢?1、 创建一个Handler,关联到线程B的Looper,这里有两个方案:直接再Handler构造函数中传入B的looper、 再线程B中创建Looper再从线程A中获取到该Handler B;2、 生成需要的Message消息;3、 通过和线程B的Looper关联的Handler B,将消息发送出去;

weixin_39702448的博客 761

深入理解 Android 消息机制

在 Android 开发中,Handler、Message 和 Looper 构成的消息处理机制是框架的核心基础。尽管相关解析已有很多,但“知其然,知其所以然”的探究永不过时。理解其精妙的设计思想,不仅能让我们更好地运用这套机制,更能为自身程序设计带来启发。本文旨在结合具体现象与源码原理,剖析事件驱动的本质,帮助开发者建立清晰、深刻的认识。通过阅读本文,你将掌握:线程切换的原理与实现消息循环(Message Loop)的核心工作机制如何在子线程中创建并运行消息循环如何通过链表分别实现栈(Stack)和队列(

2501_93078354的博客 797
上一篇: 如何用RecyclerView实现猫眼电影选择效果
下一篇: Android Studio NDK(C++)
动脑学院[David]
博客等级 码龄8年 44粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值