RxJava2.1.14源码学习(三)线程切换observerOn

Unity插件——WebView内嵌网页/浏览器插件 一、相关插件: 1、Embedded Browser 插件(文件夹名ZFBrowserUnity) 2、UniWebView插件 3、UnityAndroidWebviewToTexture插件 4、3D WebView for Android插件 二、遇到的问题: 3D WebView显示问题 项目中使用3D WebView存在的问题 三、插件使用 Embedded Browser 加载页面的使用 阅读详情

在上一篇我们已经介绍了线程切换两大操作符中的subscribeOn了,这一片我们来分析一下observerOn。本文重点是:
(1)observeOn是如何实现的,它是如何影响事件流的,
(2)如果我们使用多次会是什么结果,为什么?
(3)subscribeOn和observeOn的混合使用效果

如果对整体流程还不清晰的最好先去看看前面两篇:

RxJava2.1.14源码学习(一)基本流程(附带装饰者模式、观察者模式说明)
RxJava2.1.14源码学习(二)线程切换subscribeOn

单个ObserveOn

先从单个observeOn开始吧,下面是示例代码和结果:

 Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                LogUtils.e(TAG, "subscribe:" + Thread.currentThread().getName());
                emitter.onNext(1 + "");
                emitter.onComplete();
            }
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        LogUtils.e(TAG, "onSubscribe:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onNext(String result) {
                        //收到的值类型发生变化
                        LogUtils.e(TAG, "onNext:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onError(Throwable e) {
                        LogUtils.e(TAG, "onError:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onComplete() {
                        LogUtils.e(TAG, "onComplete:" + Thread.currentThread().getName());
                    }
                });

单个observeOn
发现除了subscribe,其它都是在主线程。前面的流程就不在分析了,我们从observeOn开始,我们猜测应该是新建了一个ObservableObserveOn类,然后里面的重点是重写的subscribeActual,去看一下:

@Override
protected void subscribeActual(Observer<? super T> observer) {
    if (scheduler instanceof TrampolineScheduler) {
        source.subscribe(observer);
    } else {
        //获取Worker实例
        Scheduler.Worker w = scheduler.createWorker();
        //传递订阅关系
        source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
    }
}

我们这里传入的是AndroidSchedulers.mainThread(),它的本质就是一个HandlerScheduler,至于为什么是,怎么找的,我们在介绍subscribeOn已经具体分析如何找到对应的Scheduler对象了,这里就不在说了,如果不懂可以去上一篇看一下。因为HandlerScheduler并不是TrampolineScheduler实例,所以走else中的逻辑,好像也没有什么特别的,获取一个Scheduler.Worker对象,这里是HandlerWorker实例,它的成员变量是一个在主线程的Handler对象,然后依然是正常的上游的被观察者对象订阅当前包装了下游观察者的新观察者。

然后从observeOn继续往上游到subscribeOn,我们知道他会触发onSubscribe函数,至于之前的都会被截断,就不管了,我们看下被回调的ObserveOnObserver的onSubscribe函数:

@Override
public void onSubscribe(Disposable s) {
     if (DisposableHelper.validate(this.s, s)) {
         this.s = s;
         ...
         //因为s并不是QueueDisposable实例,所以上面我们就不管了
         //这里实例化了一个队列
         queue = new SpscLinkedArrayQueue<T>(bufferSize);
         //回调下游观察者,即Observer的onSubscribe
         actual.onSubscribe(this);
     }
}

从上面的逻辑可以看出,此时onSubscribe还是运行在其它事件之前,所以依然是在主线程(即发生在订阅的过程中,而不是事件发射时)。接下来我们直接到上游源头去看事件的发射就可以了,并没有什么其它需要关注的逻辑。在源头,我们发射了以下事件:

 @Override
 public void subscribe(ObservableEmitter<String> emitter) throws Exception {
     LogUtils.e(TAG, "subscribe:" + Thread.currentThread().getName());
     emitter.onNext(1 + "");
     emitter.onComplete();
}

首先我们在发射事件之前打了一个日志(即打印出来的subscribe),根据上面的结果,当前是处于IO的子线程中,这是可以理解的,然后就是发射onNext和onComplete了,正常来说它们应该也是在IO的子线程,可是现在不是,是在主线程,我们猜测是不是在回调到ObserveOnObserver时作了什么处理,我们去看一下,它关于onNext和onComplete的实现:

@Override
public void onNext(T t) {
    //控制不二次执行
    if (done) {
         return;
    }
    //默认是0,不等于 QueueDisposable.ASYNC,所以将发射的T对象入队
    if (sourceMode != QueueDisposable.ASYNC) {
         queue.offer(t);
    }
    //重点
    schedule();
}

@Override
public void onComplete() {
    if (done) {
         return;
    }
    //次重点,后面分析onComplete时有用
    done = true;
    //重点
    schedule();
}

void schedule() {
    if (getAndIncrement() == 0) {
        //重点(参数为Runnable对象)
        worker.schedule(this);
    }
}

在回调的onNext和onComplete都没有做什么特殊处理,不过它们都调用了schedule,因为AtomicInteger的VALUE值,默认是0,我们还没有进行任何操作,所以会走到里面worker.schedule,这里的worker是HandlerWorker,我们去看一下它的schedule:

@Override
public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
    ...
    ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

    Message message = Message.obtain(handler, scheduled);
    message.obj = this; // Used as token for batch disposal of this worker's runnables.

    handler.sendMessageDelayed(message, unit.toMillis(delay));

    // Re-check disposed state for removing in case we were racing a call to dispose().
    if (disposed) {
        handler.removeCallbacks(scheduled);
        return Disposables.disposed();
    }

    return scheduled;
}

我想看到这里,大家可能就明白了吧,因为看到了熟悉的handler(前面我们说了,它是一个在主线程的Handler)、Message,把当前Runnable发送到轮训器里面,取出执行其run方法,所以onNext和onComplete都是在主线程回调的。不信我们继续去看一下,我们现在要关注的是ObserveOnObserver的run方法,因为我们上面把它的封装对象传入的轮训器中:

@Override
public void run() {
    if (outputFused) {
        drainFused();
    } else {
        //走这里
        drainNormal();
    }
}

void drainNormal() {
    int missed = 1;

    final SimpleQueue<T> q = queue;
    final Observer<? super T> a = actual;

    for (;;) {
         if (checkTerminated(done, q.isEmpty(), a)) {
             return;
         }

         for (;;) {
             boolean d = done;
             T v;

             try {
                 v = q.poll();
             } catch (Throwable ex) {
                ...
             }
             ...

             a.onNext(v);
         }

         missed = addAndGet(-missed);
         if (missed == 0) {
             break;
         }
    }
}

我们发现drainNormal就是从队列中取出参数T,然后做了一些检查,最后调用其onNext,而此时,因为经由Hanlder回调在主线程了。不过你可能会感到奇怪,这里并没有onError和onComplete的逻辑啊,不要急,我们回想一下刚才ObserveOnObserver的onComplete方法,它里面主要做了两个操作:
(1)将done置为true
(2)同样回调schedule
同理会回调到drainNormal,不过有一段代码我们刚才没有去关注:

if (checkTerminated(done, q.isEmpty(), a)) {
    return;
}

此时done为true,onNext的任务队列(即q)为empty,因为上一步因为全部循环取出回调了,那现在我们进去checkTerminated看一下:

boolean checkTerminated(boolean d, boolean empty, Observer<? super T> a) {
            if (cancelled) {
                queue.clear();
                return true;
            }
            //d为done,即true
            if (d) {
                Throwable e = error;
                //delayError为false
                if (delayError) {
                    if (empty) {
                        if (e != null) {
                            a.onError(e);
                        } else {
                            a.onComplete();
                        }
                        worker.dispose();
                        return true;
                    }
                } else {
                    //此处我们回调的是onComplete,所以e为null
                    if (e != null) {
                        queue.clear();
                        a.onError(e);
                        worker.dispose();
                        return true;
                    } else
                    if (empty) {
                        //因为此时onNext任务队列为空,所以走到这
                        a.onComplete();
                        worker.dispose();
                        return true;
                    }
                }
            }
            return false;
        }

到现在已经很明朗了,执行完全部的onNext,在回调onComplete时,返回true,所以drainNormal后面相关代码就不再执行,因为已经return了。onError也是同样的道理。同时,我们在上面代码中还发现一个问题,那就是我一回调完onComplete就把worker给dispose了,所以后面如果我们继续调用onError就不会继续执行了,因为已经停止订阅。

到这里我们就可以总结一下subscribeOn和observerOn的使用:
(1)subscribeOn只对上游有效,因为是在订阅过程中传递的,如果有多个,那么只有第一个”生效”(其实对于传递订阅关系都生效了,只是最终事件发射只体现出了最上游subscribeOn的作用)
(2)observerOn只对下游有效,因为它是在事件发射出来之后,回调事件的过程中生效的

多个observerOn

在上面我们已经总结出来了规律,那我们现在来试一试多个observerOn和subscribeOn的混合使用,并根据上面的总结作一下推测,最后运行示例代码,查看结果是否一致。

Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {

                LogUtils.e(TAG, "subscribe:" + Thread.currentThread().getName());
                emitter.onNext(1 + "");
                emitter.onComplete();
            }
        })
                //subscribeOn1
                .subscribeOn(Schedulers.io())
                //observeOn1
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Function<String, Integer>() {
                    @Override
                    public Integer apply(String s) throws Exception {
                        return Integer.parseInt(s);
                    }
                })
                //subscribeOn2
                .subscribeOn(Schedulers.newThread())
                .filter(new Predicate<Integer>() {
                    @Override
                    public boolean test(Integer integer) throws Exception {
                        return integer == 1;
                    }
                })
                 //observeOn2
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Integer>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        LogUtils.e(TAG, "onSubscribe:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onNext(Integer result) {
                        //收到的值类型发生变化
                        LogUtils.e(TAG, "onNext:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onError(Throwable e) {
                        LogUtils.e(TAG, "onError:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onComplete() {
                        LogUtils.e(TAG, "onComplete:" + Thread.currentThread().getName());
                    }
                });
    }

示例代码结合上一篇和上面关于单个observerOn的分析,我们分析:最后结果和上面单个observerOn是一致的,我们来看下结果:
多s多o1
果然是一样的,我想这个大家也很容易理解吧,subscribeOn在订阅时发起,影响事件发射时所在的线程,因为有多个,所以只有第一个真正影响到了事件所在线程,因此subscribe打印所在线程为IO线程是正常的,而当onNext和onComplete最终传递给Observer时,因为最终作了一个observeOn在主线程的操作,所以这两个回调都在主线程,至于onSubscribe是临Observer最近的生效,其它全部截断,因为我们是在主线程发起的,所以这里也是在主线程。

到这里关于线程切换的操作符分析已经完成。(#^.^#)

基于共识的捆绑算法CBBA在多无人机多任务调度中的实现 在捆绑过程中,每个无人机根据自己的任务清单和其他无人机的任务清单,确定一个被称为“束”的集合。通过合理的任务分配和无人机协作,CBBA算法能够实现最优的任务调度,提高了多无人机系统的效率和执行能力。在每一轮迭代中,无人机根据自己的当前任务和位置信息将任务清单发送给其他无人机,并接收其他无人机发送过来的任务清单。通过比较其他无人机的任务清单和自身的任务清单,每个无人机可以更新自己的任务分配情况。在这一步中,每个无人机根据自己选择的新任务,更新自身的任务清单,并将更新后的清单发送给其他无人机。 阅读详情

相关推荐

systrace分析 之 问题初步定位

如下surfaceflinger空缺点,上面对应的bufferTX 中有buffer,但VSYNC-SF 过来后,SurfaceFlinger却没有去取,检查测试app “android.settings”, 也没有GPU completion 时间长的问题,所以该部分可以请SurfaceFlinger的owner 进一步确认为何不取buffer。. GPU拿的是acquireFence HWC拿的是releaseFence, GPU是绘图,HWC是合成显示,这两个是互相等待的关系,

yangzex的专栏 3197

observeOn(AndroidSchedulers.mainThread())报错的解决办法

报错之前在使用: api 'io.reactivex.rxjava2:rxjava:2.1.7' api 'io.reactivex.rxjava2:rxandroid:2.0.1' api 'com.lzy.net:okgo:+' //版本号使用 + 可以自动引用最新版 api 'com.lzy.net:okrx2:+' api 'com.lzy.net:okserver:2.0.1'结果

fuzhongbin的博客 1万+

AWS S3文件上传性能优化:Java SDK多线程与分段上传实战指南

本文详细介绍了如何通过Java SDK优化AWS S3文件上传性能,重点讲解多线程并发上传与分段上传(Multipart Upload)技术的实战应用。通过动态调整分段策略、线程池配置及内存优化技巧,可显著提升大文件上传效率,实测显示组合方案比单线程上传提速近5倍。文章还涵盖加密上传、断点续传等高级场景解决方案,并提供了完整的性能对比测试数据。

d6e7f8的博客 1017

RxJava2.0(二)Schedulers线程调度器

上一节教程讲解了最基本的RxJava2的使用, 在本节中, 我们将学习RxJava强大的线程控制Schedulers.

m0_49508485的博客 1107

ffmpeg在windows的安装、合并、切片、.m4s、.m3u8处理

自己处理视频,我用的工具是 ffmpeg,因为涉及到.m3u8格式和.m4s格式的一些操作。ffmpeg的介绍我就不多说了,这里是它的官网地址: https://ffmpeg.org/ ,这里是文档: https://ffmpeg.org/documentation.html (看的头大,没咋看下去)...

一只爱吃糖的小羊 2689

DP链路训练从HPD触发到通道对齐:原理与调试实战

高速数字接口的稳定工作依赖物理层信号的精确协商,链路训练正是DisplayPort接口中确保源端与接收端在速率、摆幅和时序上达成一致的关键机制。HPD作为链路状态变化的触发信号,与通道对齐一同构成了训练流程的首尾环节。理解从时钟恢复到均衡再到多通道对齐的完整过程,有助于快速定位显示异常问题。在工程实践中,借助DPCD寄存器交互和AUX通道日志,可以系统性地排查训练失败原因。围绕链路训练全流程进行剖析,重点探讨HPD触发机制、训练图案选择、寄存器配置以及链路降级策略,为显示方案开发与调试提供工程参考。

weixin_30110543的博客 235

关于RxJava的简单用法(三)

线程控制 —— Scheduler RxJava在不指定线程的情况下,发起时间和消费时间默认使用当前线程. Observable.just(student1, student2, student2) //使用map进行转换,参数1:转换前的类型,参数2:转换后的类型 .map(new Func1() {

QQNKW的博客 874

【华为Hilink SDK Linux系统开发】目录 开源一小步

目录 【华为Hilink开发】第一章:华为hilink平台简介及开发流程 【华为Hilink开发】第二章:华为hilink SDK网络适配、网关添加删除 【华为Hilink开发】第三章:华为hilink SDK子设备添加、删除 【华为Hilink开发】第四章:华为hilink SDK子设备控制及同步 【华为Hilink开发】第五章:华为hilink SDK适配遇到的问题 ...

开源一小步 3620

Android RxJava生命周期管理解决方案整理

一、为什么要做生命周期管理? Observable.create(new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> emitter) throws Exception { //TODO } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .

残风乱了温柔的博客 2885

【连接器专题】 EIA-364 系列标准的完整列表

EIA-364 系列标准在电子工业中具有重要的地位,为电子连接器和互连系统的测试和验证提供了统一的、可操作的流程和方法,有助于提高电子产品的质量和可靠性,促进了电子工业的发展

一点硬件 2660

PCL 区域生长分割【2026最新版】

区域生长算法原理介绍、代码实现以及一个具体实验场景的应用案例进一步实现将分割结果的分类保存至本地文件夹。最近一次更新时间为:2026年5月4日。

点云侠的博客 40万+

Android RxJava2 背压详解

一、前言 对于异步订阅关系,存在被观察者发送事件速度 与观察者接收事件速度 不匹配的情况,被观察者 发送事件速度太快,而观察者 来不及接收所有事件,从而导致观察者无法及时响应 / 处理所有发送过来事件的问题,最终导致缓存区溢出、事件丢失 & OOM,这个时候就需要使用到背压了。 二、详解 2.1、定义 背压是一种控制事件流速的策略,它的作用是在异步订阅关系中,控制事件发送和...

lerendan的博客 1222

3. Matérn协方差模型与随机偏微分方程

对应的高斯马尔可夫随机场 (GMRF)可以通过使用某种随机偏微分方程(SPDE)明确构造,当由高斯白噪声驱动时,随机偏微分方程的解是具有 Matérn 协方差函数的高斯场(GF)

weixin_40614311的博客 1747

坑- observeOn(AndroidSchedulers.mainThread())

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34023982的博客 1113

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

【Android】RxJava的使用(四)线程控制 —— Scheduler

转自:http://www.jianshu.com/p/ecfb9d68d2a2 前言 经过前几篇的介绍,对RxJava对模式有了一定的理解:由Observable发起事件,经过中间的处理后由Observer消费。(对RxJava还不了解的可以出门左拐) 之前的代码中,事件的发起和消费都是在同一个线程中执行,也就是说之前我们使用的RxJava是同步的~~~ 观察者模式本身的目的不就

linder_qzy的专栏 560

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

observeOn(AndroidSchedulers.mainThread())报错

   主要因为rxjava1和rxjava2导包问题。用rxjava1的童孩在创观察者和被观察者的时候导rx包。用rxjava2的童孩导io.reaxtive包。

Diegoing的博客 2395

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

android rxjava 多线程,Rxjava(4) 线程切换 - AndroidSchedulers.mainThread()

observable的.observeOn(AndroidSchedulers.mainThread())方法指定最后的结果处于哪个线程中,是通过handler发送消息到主线中的MainLooper中。为Message的callback设置对象。在handler的dispatchMessage方法中会进行拦截,不会调用handler 的handleMessage方法。流程图以一下简单示例看下执行过...

weixin_33305349的博客 1324

Android框架源码分析——RxJava&Schedule源码分析

3、线程切换 使用实例 Observable.create(object:ObservableOnSubscribe<String>{ override fun subscribe(e: ObservableEmitter<String>) { e.onNext("A") } }) .subscribeOn(Schedulers.io(...

浅谈Android 635

RxJava 线程池 compose 与 ObservableTransformer

RxJava 线程池 线程控制——Scheduler RxJava中的多线程操作主要是由Scheduler集合提供的。在RxJava中,我们无法直接访问或操作线程。如果想要使用线程的话,必须要通过内置的Scheduler来实现。 在RxJava 中,Scheduler ——调度器,相当于线程控制器,RxJava 通过它来指定每一段代码应该运行在什么样的线程。RxJava 已经内置了几个 Scheduler ,它们已经适合大多数的使用场景。 Schedulers.io():它适用于非C...

qq_30998053的博客 1646

Android开发面试之RxJava

好文推荐: 作者:xiangcman 大家都知道RxJava上手是非常难的一个框架,为什么说是难呢,因为它的功能非常强大,各种操作符让人很难上手,搭配使用带生命周期的框架有RxLife等。以至于后面出了很多类似Rxjava的框架,有RxAndroid,我们用的RxJava切换主线程就是出自该框架,后面ACC架构中有LiveData、Lifecycle、包括后面协成中出来的flow异步流,都是向Rxjava靠拢,不过ACC出来的这些框架他们最大的特点就是操作简单,上手简单。所以RxJava强大的框架背后.

鸿蒙开发知识记录 4032

RxJava中遇到的坑持续更新...

RxJava中遇到的坑 1.线程无限创建 在轮询的请求中使用.subscribeOn(Schedulers.io())可能会导致无限创建线程问题。 Schedulers是RxJava的主要组件之一。负责在不同线程上执行Observable的操作,便于将耗时任务分摊到其他线程。 我们这里需要连了解下Schedulers下的Scheduler的属性: IOScheduler 最常见的调度器之一。用于IO相关操作。比如网络请求和文件操作。IO 调度器背后由线程池支撑。 它首先创建一个工作线程,可以复用于其他操作。

Darksider博客 1976

RX系列五 | Schedulers线程控制

RX系列五 | Schedulers线程控制 在我们上一篇文章中的,我们的小例子里有这么一段代码 //网络访问 .observeOn(Schedulers.io()) 事实上,我们在使用网络操作的时候,便可以控制其运行在哪个线程中,而Schedulers类,有四个方法,分别是 Schedulers.immediate()...

weixin_33860147的博客 393

RxJava开发精要7 - Schedulers-解决Android主线程问题

本章中,我们提升标准看看如何使用RxJava的调度器来处理多线程和并发编程的问题。我们将学习到如何以响应式的方式创建网络操作,内存访问,以及耗时任务。

Mr.Simple的专栏 1万+

RXJava之线程控制Scheduler(四)

阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 线程控制:Scheduler 除了灵活的变换,RxJava 另一个牛逼的地方,就是线程的自由控制。 1) Scheduler 的 API 前面讲到了,可以利用 subscribeOn() 结合 observeOn() 来实现线程控制,让事件的产生和...

AndroidAlvin的博客 191

rxjava的subscribeon和observeon的线程区分

subscribeOn用于指定Observable开始执行(即被订阅时)所在的线程,通常影响数据发射的源头,比如网络请求或数据库操作所在的线程。而observeOn则影响下游操作符和订阅者的执行线程,比如更新UI需要在主线程执行。

罗春娥的技术园 204

RxJava 和 RxAndroid 五(线程调度)

对rxJava不了解的同学可以先看 RxJava 和 RxAndroid 一 (基础)RxJava 和 RxAndroid 二(操作符的使用)RxJava 和 RxAndroid 三(生命周期控制和内存优化) RxJava 和 RxAndroid 四(RxBinding的使用)   本文将有几个例子说明,rxjava线程调度的正确使用姿势。 例1 Observable ...

weixin_33766168的博客 243
上一篇: RxJava2.1.14源码学习(二)线程切换subscribeOn
下一篇: Android入坑react-native:关于从原生数据流到flux、redux的演化
cp_Mark
博客等级 码龄10年 22粉丝 44原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值