RxJava(十四)interval、takeWhile操作符实现获取验证码功能

RxJava系列文章目录导读:

一、RxJava create操作符的用法和源码分析
二、RxJava map操作符用法详解
三、RxJava flatMap操作符用法详解
四、RxJava concatMap操作符用法详解
五、RxJava onErrorResumeNext操作符实现app与服务器间token机制
六、RxJava retryWhen操作符实现错误重试机制
七、RxJava 使用debounce操作符优化app搜索功能
八、RxJava concat操作处理多数据源
九、RxJava zip操作符在Android中的实际使用场景
十、RxJava switchIfEmpty操作符实现Android检查本地缓存逻辑判断
十一、RxJava defer操作符实现代码支持链式调用
十二、combineLatest操作符的高级使用
十三、RxJava导致Fragment Activity内存泄漏问题
十四、interval、takeWhile操作符实现获取验证码功能
十五、RxJava线程的自由切换


现在主流的App中,使用手机验证码的场景越来越多,比如验证码登陆、验证码重置/找回密码等。

一般功能流程如下:

1,防止获取验证码按钮在短时间内被点击多次

2,调用获取验证码接口(提示loading,防止界面被操作)

3,验证码获取成功开始倒计时,此时按钮不可被点击

4,倒计时完毕后,点击可以再次被点击

在Android中实现该功能的方案也有很多,比如Timer、CountDownTimer、Handler等。

接下来主要是讲如何使用RxJava来实现该功能。

点击按钮调用获取验证码接口可能有两种交互

1,调用获取验证码接口,如果是弹出一个loading框,这样的最简单,因为用户不能再次点这个按钮了
(接口返回成功则开始倒计时,失败则loading消失按钮可以再次被点击)

2,调用获取验证码接口,不实用loading框的方式,直接让按钮不可点,按钮的文字变成"正在获取..."之类的。
接口调用成功则开始倒计时,接口失败则把按钮设置为可点

这两种方式用RxJava都可以轻松实现。第一种方式主流一些,以下讲第一种交互实现方式。

代码如下:

RxView.clicks(mBtn)
        .throttleFirst(1, TimeUnit.SECONDS)
        .flatMap(new Func1<Void, Observable<Boolean>>() {
            @Override
            public Observable<Boolean> call(Void aVoid) {
                //弹出loading框(省略)
                //调用获取验证码接口 (省略)
                return Observable.just(true);
            }
        })
        .flatMap(new Func1<Boolean, Observable<Boolean>>() {
            @Override
            public Observable<Boolean> call(Boolean aBoolean) {
                //验证码获取成功,把按钮置为不可点
                mBtn.setEnabled(false);
                //关闭loading框(省略)
                return intervalButton();
            }
        })
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<Boolean>() {
            @Override
            public void call(Boolean aBoolean) {
                Log.e("FetchPhoneCodeFragment", "subscribe call");
            }
        }, new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                throwable.printStackTrace();
                //关闭loading框(省略)
                //出错后把按钮设置为可用
                mBtn.setEnabled(true);
                Toast.makeText(getActivity(), throwable.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });

获取验证码成功后,开始倒计时

   public Observable<Boolean> intervalButton() {
        //如果使用Observable.interval(interval, TimeUnit),会默认延迟interval执行
        return Observable.interval(0, 1000, TimeUnit.MILLISECONDS) // ,每秒发射一次事件
                //如果aLong小于等于我们设定的秒数,发射事件
                .takeWhile(new Func1<Long, Boolean>() {
                    @Override
                    public Boolean call(Long aLong) {
                        return aLong <= INTERVAL_TIME;
                    }
                })

                //可以通过filter达到takeWhile相同的效果
//                .filter(new Func1<Long, Boolean>() {
//                    @Override
//                    public Boolean call(Long aLong) {
//                        return aLong <= INTERVAL_TIME;
//                    }
//                })
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(new Func1<Long, Observable<Boolean>>() {
                    @Override
                    public Observable<Boolean> call(Long aLong) {
                        long diff = INTERVAL_TIME - aLong;
                        Log.e("FetchPhoneCodeFragment", "thread name:" + Thread.currentThread().getName() + ",aLong:" + aLong + ",diff:" + diff);
                        mBtn.setText(diff + "s");
                        //倒计时完毕后,按钮可以再次被点击,重新设置按钮文案
                        if (diff == 0) {
                            mBtn.setEnabled(true);
                            mBtn.setText("获取验证码");
                        }
                        return Observable.just(true);
                    }
                });
    }

上面的注释也很详细,这里就简单的概述下:

扫描二维码关注公众号,回复: 1016829 查看本文章
通过RxView的throttleFirst避免在短时间内按钮被点击多次

调用获取验证码接口把按钮置为不可用,并且弹出loading框,如果接口调用成功了则开始倒计时,如果失败的话关闭loading框,把按钮设置为可用。

倒计时功能,到了我们预设的秒数,把按钮置为可用, 否则按钮不可用。

需要注意的是interval定时发射数据都会执行subscribe action;而且不取消的话,interval会一直发射数据,所以使用takeWhile、filter来作为条件判断。

另一方面,在onDestroy的时候需要把Subscription反注销:

@Override
public void onDestroy() {
    super.onDestroy();
    if (mSubscription != null && !mSubscription.isUnsubscribed()) {
        mSubscription.unsubscribe();
    }
}

源代码地址 https://github.com/chiclaim/AndroidAll

猜你喜欢

转载自blog.csdn.net/johnny901114/article/details/79037306