Rxjava2源码浅析(一)

Android之Rxjava2-源码解析,学海无涯 final Function<? super T, ? extends R> mapper; MapSingleObserver(SingleObserver<? super R> t, Function<? super T, ? extends R> mapper) { this.t = t; this.mapper = mapper; } @Override public void onSubscribe(Disposable d) { t.onSubscribe(d 阅读详情

面试的时候被问道各种框架的原理架构,也是很尴尬,自以为写的代码不少,用过的框架也不少,深入的去研究源码的还真是不多,也是给自己敲了一个警钟,今天就来尝试剖析一下Rxjava2的源码,水平有限,就先看一下基础的用法相关,一些难度更高的操作符就慢慢来分析吧。
就按照平时使用的顺序来分析:

一、初始化Observerble

基本使用实例:

Observable<String> observable= Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext("aaa");
            }
        });

先看一下内部的参数 ObservableOnSubscribe<>() 。

public interface ObservableOnSubscribe<T> {
    void subscribe(ObservableEmitter<T> e) throws Exception;
}

就是一个接口,这里用的就是它的一个匿名实现类。而接口内部的方法中我们看到ObservableEmitter<> 是一个Rxjava2新推出的类,俗称发射器。

public interface ObservableEmitter<T> extends Emitter<T> {

    /**
     * Sets a Disposable on this emitter; any previous Disposable
     * or Cancellation will be unsubscribed/cancelled.
     * @param d the disposable, null is allowed
     */
    void setDisposable(Disposable d);

    /**
     * Sets a Cancellable on this emitter; any previous Disposable
     * or Cancellation will be unsubscribed/cancelled.
     * @param c the cancellable resource, null is allowed
     */
    void setCancellable(Cancellable c);

    /**
     * Returns true if the downstream disposed the sequence.
     * @return true if the downstream disposed the sequence
     */
    boolean isDisposed();

    /**
     * Ensures that calls to onNext, onError and onComplete are properly serialized.
     * @return the serialized ObservableEmitter
     */
    ObservableEmitter<T> serialize();
}

这里面几个回调方法的作用注释也说的很清楚了就不多说了。
它继承自Emitter

public interface Emitter<T> {

    /**
     * Signal a normal value.
     * @param value the value to signal, not null
     */
    void onNext(@NonNull T value);

    /**
     * Signal a Throwable exception.
     * @param error the Throwable to signal, not null
     */
    void onError(@NonNull Throwable error);

    /**
     * Signal a completion.
     */
    void onComplete();
}

可以看到,这里面就是我们比较熟悉的next、complete、error三个回调方法了。其实这个create方法内部的参数就是两个接口的回调,理解就行了,然后看一下create方法。

 public static <T> Observable<T> create(ObservableOnSubscribe<T> source) {
        ObjectHelper.requireNonNull(source, "source is null");
        return RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));
    }

requireNonNull是很好理解的,看名字也能猜测出是测试传进来的ObservableOnSubscribe是否为空

  public static <T> T requireNonNull(T object, String message) {
        if (object == null) {
            throw new NullPointerException(message);
        }
        return object;
    }

而源码也验证了我们的想法。关键是后面一句,先看一下具体的方法实现。

public static <T> Observable<T> onAssembly(@NonNull Observable<T> source) {
        Function<? super Observable, ? extends Observable> f = onObservableAssembly;
        if (f != null) {
            return apply(f, source);
        }
        return source;
    }

一句一句来分析:

public interface Function<T, R> {
    /**
     * Apply some calculation to the input value and return some other value.
     * @param t the input value
     * @return the output value
     * @throws Exception on error
     */
    @NonNull
    R apply(@NonNull T t) throws Exception;
}

这里的Function也是一个接口,作用也很明显,将T类型的数据转化成R类型数据。那是我们在使用到

        observable.map(new Function<String, Object>() {
            @Override
            public Object apply(@NonNull String s) throws Exception {
                return null;
            }
        })

类似这种类型转换的语句时候才会用到,这里我们先不管它,一开始是默认为null的,所以这个方法最后就会return source;就是将括号中的new ObservableCreate(source)原样返回。这个ObservableCreate又是什么呢?

public ObservableCreate(ObservableOnSubscribe<T> source) {
        this.source = source;
    }

源码比较长我们就之看一下它的构造函数就可以了,目前只需要知道这是一个Observerble的子类就可以了,至于Observerble这个类,等到大概摸清楚了事件流程再回头来分析。所以到现在我们的第一步初始化就算是分析完了流程。

二、初始化一个Observer

用法示例:

Observer<String> observer=new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                subscription=d;
            }

            @Override
            public void onNext(String value) {
                LogUtil.log(TAG,"  "+value);
            }

            @Override
            public void onError(Throwable e) {
            }

            @Override
            public void onComplete() {
                LogUtil.log(TAG,"complete");
            }
        };

这个分析就要简单很多了,Observer只是一个简单的接口,这里也只是具体实现了一下接口回调。

public interface Observer<T> {

    /**
     * Provides the Observer with the means of cancelling (disposing) the
     * connection (channel) with the Observable in both
     * synchronous (from within {@link #onNext(Object)}) and asynchronous manner.
     * @param d the Disposable instance whose {@link Disposable#dispose()} can
     * be called anytime to cancel the connection
     * @since 2.0
     */
    void onSubscribe(Disposable d);

    /**
     * Provides the Observer with a new item to observe.
     * <p>
     * The {@link Observable} may call this method 0 or more times.
     * <p>
     * The {@code Observable} will not call this method again after it calls either {@link #onComplete} or
     * {@link #onError}.
     *
     * @param t
     *          the item emitted by the Observable
     */
    void onNext(T t);

    /**
     * Notifies the Observer that the {@link Observable} has experienced an error condition.
     * <p>
     * If the {@link Observable} calls this method, it will not thereafter call {@link #onNext} or
     * {@link #onComplete}.
     *
     * @param e
     *          the exception encountered by the Observable
     */
    void onError(Throwable e);

    /**
     * Notifies the Observer that the {@link Observable} has finished sending push-based notifications.
     * <p>
     * The {@link Observable} will not call this method if it calls {@link #onError}.
     */
    void onComplete();

}

不过这里和Rxjava1也是有些区别的,多了一个onSubscribe 注释也说的很清楚,用于随时取消订阅。
第二步很轻松,下面看一下第三步

三、建立订阅关系

用法示例:

observable.subscribe(observer);

这里我们就只分析最简单的一种,看一下源码:

@SchedulerSupport(SchedulerSupport.NONE)
    @Override
    public final void subscribe(Observer<? super T> observer) {
        ObjectHelper.requireNonNull(observer, "observer is null");
        try {
            observer = RxJavaPlugins.onSubscribe(this, observer);

            ObjectHelper.requireNonNull(observer, "Plugin returned null Observer");

            subscribeActual(observer);
        } catch (NullPointerException e) { // NOPMD
            throw e;
        } catch (Throwable e) {
            Exceptions.throwIfFatal(e);
            // can't call onError because no way to know if a Disposable has been set or not
            // can't call onSubscribe because the call might have set a Subscription already
            RxJavaPlugins.onError(e);

            NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");
            npe.initCause(e);
            throw npe;
        }
    }

第一句还是一样,判断是否为空,平时自己写代码也要像这样注意代码的健壮性。
重点就是这三句了。

 observer = RxJavaPlugins.onSubscribe(this, observer);

 ObjectHelper.requireNonNull(observer, "Plugin returned null Observer");

 subscribeActual(observer);

一句一句来看:

 public static <T> Observer<? super T> onSubscribe(@NonNull Observable<T> source, @NonNull Observer<? super T> observer) {
        BiFunction<? super Observable, ? super Observer, ? extends Observer> f = onObservableSubscribe;
        if (f != null) {
            return apply(f, source, observer);
        }
        return observer;
    }

在这个onsubscribe中是不是觉得有些眼熟?就跟刚刚的onAssenmbly几乎一样,由于我们没有其它的功能,所以这里onObservableSubscribe也是null,也是返回原值,下面的requireNonNull我们也见过了,又验证一遍是否为空,因为如果我们加入了Function函数,上面就不会返回原来的observer了,所以还要再验证一遍。
于是就到了最后一句

protected abstract void subscribeActual(Observer<? super T> observer);

???
怎么是个abstract方法?那么它是在哪实现的呢?
回想看我们的observable初始化过程。哪里出的问题呢?就是我们一开始没有分析的ObservableCreate,我们在初始化的时候就将一个ObservableCreate类向上转型赋值给了Observerble,所以方法的具体实现也就在ObservableCreate里了。
继续跟进。果不其然:

@Override
    protected void subscribeActual(Observer<? super T> observer) {
        CreateEmitter<T> parent = new CreateEmitter<T>(observer);
        observer.onSubscribe(parent);

        try {
            source.subscribe(parent);
        } catch (Throwable ex) {
            Exceptions.throwIfFatal(ex);
            parent.onError(ex);
        }
    }

还是一句一句来看

CreateEmitter<T> parent = new CreateEmitter<T>(observer);

又是一个新的类

    static final class CreateEmitter<T>
    extends AtomicReference<Disposable>
    implements ObservableEmitter<T>, Disposable {


        private static final long serialVersionUID = -3434801548987643227L;

        final Observer<? super T> observer;

        CreateEmitter(Observer<? super T> observer) {
            this.observer = observer;
        }

        @Override
        public void onNext(T t) {
            if (t == null) {
                onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
                return;
            }
            if (!isDisposed()) {
                observer.onNext(t);
            }
        }

        @Override
        public void onError(Throwable t) {
            if (t == null) {
                t = new NullPointerException("onError called with null. Null values are generally not allowed in 2.x operators and sources.");
            }
            if (!isDisposed()) {
                try {
                    observer.onError(t);
                } finally {
                    dispose();
                }
            } else {
                RxJavaPlugins.onError(t);
            }
        }

        @Override
        public void onComplete() {
            if (!isDisposed()) {
                try {
                    observer.onComplete();
                } finally {
                    dispose();
                }
            }
        }

        @Override
        public void setDisposable(Disposable d) {
            DisposableHelper.set(this, d);
        }

        @Override
        public void setCancellable(Cancellable c) {
            setDisposable(new CancellableDisposable(c));
        }

        @Override
        public ObservableEmitter<T> serialize() {
            return new SerializedEmitter<T>(this);
        }

        @Override
        public void dispose() {
            DisposableHelper.dispose(this);
        }

        @Override
        public boolean isDisposed() {
            return DisposableHelper.isDisposed(get());
        }
    }

所以我们的前两句

CreateEmitter<T> parent = new CreateEmitter<T>(observer);
observer.onSubscribe(parent);

所以我们的前两局就是回调了onSubscribe接口,从而将这个CreateEmitter类型转型成Disposable输出了。而CreateEmitter的初始化参数又是observer本身,所以大体上可以看成回调了另一个格式的自己。。。然后一般可用于自杀(取消订阅)。。。
然后就来到的最后一句

source.subscribe(parent);

这里的source就是

new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext("test");
            }
        }

刚刚我们初始化observable传入的。这个parent->这里的参数e。于是就这样完成了Observerble和Observer的绑定,也就能实现接口回调了。

没有任何其它功能,只是走了一边最基本流程的Rxjava源码,后面还会继续更新的。

Rxjava2 源码解析(一) 到目前为止,主流框架Rxjava+Retrofit 已经越来越受到各位开发者的喜爱,虽然之前也用过,但是一直没有弄清里面的原理,导致面试阶段被面试官全程吊打,痛定思痛,下决心好好沉下心来,整理一些知识,与诸君共勉。 好了,闲话少说,下面的一段代码是一段简单的被观察者与观察者的订阅过程: private void test() { Observable<String&gt... 阅读详情

相关推荐

RxJava2 源码解析(一)

概述最近事情太多了,现在公司内部的变动,自己岗位的变化,以及最近决定找工作。所以博客耽误了,准备面试中,打算看一看RxJava2的源码,遂有了这篇文章。不会对RxJava2的源码逐字逐句的阅读,只寻找关键处,我们平时接触得到的那些代码。 背压实际中接触较少,故只分析了Observable. 分析的源码版本为:2.0.1我们的目的: 知道源头(Observable)是如何将数据发送出去的。 知道终

zxt0601的博客 1万+

Android进阶知识:RxJava相关

1. 前言 RxJava核心功能是一个用来完成异步操作的库,相对于其它异步操作的方法,RxJava的API使用更加的简洁。并且RxJava中还提供了很多功能强大的操作符,帮助我们解决很多原本复杂繁琐的代码逻辑,提高了代码质量。RxJava的实现是基于观察者模式,观察者模式中以下有三个比较重要的概念: 被观察者(Observable) 观察者(Observer) 订阅(subscribe) 被观察者是事件的发起者,被观察者与观察者建立订阅关系后,被观察者发送事件,观察者才能接收到事件。 2. 基础使用 R

鸿蒙开发知识记录 471

RxJava2 源码解析(二)

概述 承接上一篇RxJava2 源码解析(一), 本系列我们的目的: 知道源头(Observable)是如何将数据发送出去的。 知道终点(Observer)是如何接收到数据的。 何时将源头和终点关联起来的 知道线程调度是怎么实现的 知道操作符是怎么实现的 本篇计划讲解一下4,5. RxJava最强大的莫过于它的线程调度 和 花式操作符。 map操作符 map是一个高频的操作符,我

zxt0601的博客 8514

盘点常用的Android开发库(5) -- Rxjava2原理分析

一、简介 这篇文章不会介绍Rxjava2的使用和操作符等内容,相信大家也或多或少有所了解,网上也有很多此类的文章,如果从未使用过的Rxjava或对其不怎么了解的,可以先简单的了解下Rxjava2的基本使用。 本篇文章主要是跟着源码分析和梳理Rxjava的构建、订阅、事件分发流程。 ...

ledding的博客 810

Android之Rxjava2-源码解析

我这里整理了一份完整的学习思维以及Android开发知识大全PDF。当然实践出真知,即使有了学习线路也要注重实践,学习过的内容只有结合实操才算是真正的掌握。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!**[外链图片转存中…(img-dS0TVOXT-1713227493872)][外链图片转存中…(img-Dcw07gAB-1713227493873)][外链图片转存中…(img-2sENQ7XK-1713227493874)]

2401_84123113的博客 1056

深入解析RxJava源码(一)Observable对象的构建

现在RxJava在Android界已经是无人不知无人不晓的技术了,如果你还不知道RxJava是什么,推荐你去看给 Android 开发者的 RxJava 详解,而本篇博客主要是通过一些例子和源码分析以加深对RxJava的理解,所以看本篇博客时请确保你已对RxJava有了一定的了解。

Martino的专栏 2738

Rxjava 2 的源码解析

从Demo到原理 Observable.create(new ObservableOnSubscribe() { @Override public void subscribe(ObservableEmitter e) throws Exception { e.onNext(1); e.onNext(2); e.onNext(3);

qq_39431405的博客 639

保姆级教程:Halcon 3D点云匹配与差异分析避坑指南(从采样到可视化)

本文提供了一份详细的Halcon 3D点云匹配与差异分析实战指南,涵盖从模型准备到可视化的全流程。重点解析了STL文件读取单位误区、点云采样密度优化、表面模型创建技巧以及差异分析的专业手法,帮助用户避开常见陷阱,提升工业检测效率与精度。

weixin_33728268的博客 402

Rxjava2源码浅析(一)

前言 一直对Rxjava的实现很好奇,这次刚好有时间就看下它的源码,并把其中的思路记录下来,加深印象。先是看了其它人的分析后自己又去看了源码,说实话源码还是蛮绕的,可能自己还是对观察者模式理解的不够深刻吧… 这次主要了解三点: 1. 知道源头(Observable)是如何将数据发送出去的。 2. 知道终点(Observer)是如何接收到数据的。 3. 何时将源头和终点关联起来的。

z2wenfa的博客 319

新手必看:如何用T265和ROS实现无人机室内精准定位(附PX4参数配置)

本文详细介绍了如何使用Intel Realsense T265和ROS实现无人机室内精准定位,包括硬件准备、ROS数据流架构设计、PX4参数配置及系统验证。特别针对视觉SLAM技术在室内环境中的应用,提供了从零搭建到进阶优化的全流程解析,适合无人机开发新手快速上手。

weixin_30642561的博客 404

单细胞转录组肿瘤细胞识别:inferCNV与CopyKAT实战解析

单细胞转录组测序(scRNA-seq)是解析肿瘤微环境的重要技术,但如何从海量细胞中准确区分恶性细胞与正常细胞,始终是数据分析的关键难题。传统marker注释在异质性肿瘤中常失效,而基于拷贝数变异(CNV)的推断方法为这一问题提供了更稳健的解决路径。CNV通过检测染色体大片段的增益或缺失,可在无先验标记的情况下识别恶性细胞,尤其适合实体瘤研究。inferCNV利用染色体滑动窗口分析相对表达量,生成直观的CNV热图;CopyKAT则结合高斯混合模型对每个细胞直接分类,输出肿瘤/正常/非整倍体标签。本文从原理出

weixin_31236101的博客 249

浅析RxJava 1.x&2.x版本使用区别及原理(一):Observable、Flowable等基本元素源码解析

RxJava开源框架的风靡程度在Github上无需多言,它带来的响应式编程模式和线程随意切换、嵌套请求、背压等功能给了开发者耳目一新的体验,更是成为了大多数APP中常用的RxJava+Okhttp/Retrofit+MVP/MVVM/Clean黄金组合中的一员。我犹记得知乎团队在去年线下还开展过一次线下RxJava交流会,邀请了扔物线讲解相关知识及体验,可见各大厂对此的重视度。如非要列举一个RxJ...

lemonGuo的大脑皮层、小脑、海马体、杏仁核..... 6045

YOLOv11 改进 | 模块即插即用

YOLOv11 是一种单阶段目标检测算法,其核心设计目标在于实现检测速度与精度的平衡。其架构延续了 YOLO 系列“端到端”检测的思路,通过统一的网络结构完成特征提取、多尺度特征融合及最终的边界框预测与分类任务。骨干网络(Backbone)颈部网络(Neck)和检测头(Head)。骨干网络负责从输入图像中提取多层次的语义特征。

u012133341的博客 3117

一看就懂的RxJava源码分析

本文对于RxJava的源码进行分析,以更加彻底的了解RxJava的实现思路。

Mekeater的博客 2392

【K8S系列】Kubernetes 中 Service 的流量不均匀问题【已解决】

在 Kubernetes 中,Service 是一种抽象,用于定义一组 Pod 的访问策略。当某些 Pod 接收的流量过多,而其他 Pod 的流量较少时,可能会导致负载不均衡。这种情况不仅影响性能,还可能导致某些 Pod 过载,影响应用的可用性。本文将详细分析此问题的原因及其解决方案。

颜淡慕潇 8737

适航规章、标准,书籍资料下载汇总(增加DO-160G中英文版)

很多读者之前问我有没有DO-160G的资源,现在终于找到啦!如果大家有其他想要的资源,可以在留言区告诉我,有空我会帮大家找找看~文末有下载链接:更新日志:2024-9-28:初版。2024-10-21:增加DO-330英文版PDF下载。2024-10-30:增加DO-160G中文版(非机翻)和英文版PDF下载。以下是一些收集到的适航相关的资料,分享给大家。(如果没有你想要的资料,请留言,等找到会及时更新!

qq_41854911的博客 2756

RxJava2最全面、最详细的讲解(一)

一、概述 提高开发效果,降低维护成本一直是团队追求的宗旨。现在安卓里面RxJava2+Retrofit+OKHttp的网络请求框架是最流行的,几乎一统江湖,配合响应式式编程的RxJava使用也越来越多。如果英文比较好而且有兴趣的可以到官网学习:ReactiveX的官方网站。(源码和其他相关链接在文章最后给出) RxJava到底是什么?       RxJava在Git...

qq_32534441的博客 9529

跨平台CMake AutoSAR

AutoSAR是汽车行业采用的一种软件架构标准,它旨在提供一个通用的软件平台,使得汽车软件的开发和集成变得更加高效和灵活。总结一下,CMake AutoSAR是一个强大的跨平台工具,为AutoSAR开发提供了便利。通过简单的配置文件和命令,开发人员可以实现高效、灵活的跨平台开发,并加快自动驾驶系统的开发进程。上述示例中,我们定义了一个名为AutoSAR的项目,指定了源代码文件的路径和依赖库的路径。通过简单的配置文件和命令,开发人员可以实现高效、灵活的跨平台开发,并加快自动驾驶系统的开发进程。

EcmFsharp的博客 232

深入RxJava2 源码解析(一)

深入RxJava2 源码解析(一) 本文作者JasonChen,原文地址: http://chblog.me/2018/12/19/rxjava2 源码解析(一)/ ReactiveX 响应式编程库,这是一个程序库,通过使用可观察的事件序列来构成异步和事件驱动的程序。 其简化了异步多线程编程,在以前多线程编程的世界中,锁、可重入锁、同步队列器、信号量、并发同步器、同步计数器、并行框架等都是具有...

aoho求索 649

华为OD机试真题 新系统【查找温度记录统计信息】

华为OD机试 新系统真题【查找温度记录统计信息】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【查找温度记录统计信息】多语言题解。

qq_45776114的博客 486

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

RxJava源码浅析

rxjava

FSC的专栏 5358

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

RxJava2源码分析(一):基本流程分析

前言:到现在这个阶段,网上关于RxJava2源码分析的文章已经满天飞了,我写这篇文章的目的并不是说我会分析的比他们好,比他们透彻,这篇文章的目的只是单纯的记录自己分析RxJava2源码的成功及收获。 概述   对于一个编程人的技术成长,一般会经历三个阶段,首先是学会使用开源库,然后是知道开源库的原理,最后就是自己写一个开源库。虽然在日常的开发中使用RxJava2已经达到了得心应手的地步,但是...

fnhfire_7030的博客 1161

jre9下载 jdk9

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

efm32 入门.pdf

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

友好 RxJava2.x 源码解析(一)基本订阅流程

系列文章: 友好 RxJava2.x 源码解析(一)基本订阅流程 友好 RxJava2.x 源码解析(二)线程切换 友好 RxJava2.x 源码解析(三)zip 源码分析 本文掘金地址:友好 RxJava2.x 源码解析(一)基本订阅流程 本文基于 RxJava 2.1.3 前言 示例代码 订阅流程源码解析 订阅流程 Observable#subscribe(Observer) ...

joker 1556

RxJava2 源码分析一

文章目录 前言 RxJava2 介绍 RxJava2 使用 带问题看源码 总结 前言 在OkHttp3+Retrofit2+RxJava2 它是怎么在起作用的,为什么它会受到如此的欢迎,先通过分析如何发送数据,还有接收数据这块内容。 本文是居于此版本分析的 implementation "io.reactivex.rxjava2:rxjava:2.2.8" implement...

shenshizhong的专栏 356
上一篇: Android开发--IM聊天项目(五)--进程保活踩坑
下一篇: Rxjava2源码浅析(二)
CallMeSP
博客等级 码龄10年 48粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值