RxJava 五:场景模拟

本文详细介绍了RxJava在Android开发中的多种应用场景,包括计算平均值、搜索联想优化、防止重复点击、网络请求处理、页面轮询、错误重试、输入验证、数据加载策略、token刷新、屏幕旋转恢复任务、网络重连以及验证码发送模拟,提供了具体的解决方案和代码示例。
摘要由CSDN通过智能技术生成

目录

1.计算一段时间内的平均值

2.优化搜索联想功能

3.优化多次重复点击

4.RxJava + Retrofit + OkHttp

5.页面轮询请求

6.接口重新请求(retry)

7.本地验证输入有效性

8.优先加载本地缓存,再读取网络数据

方案一:concat

方案二:concatEager

方案三:merge

方案四:publish + merge

9.网络请求中发现token过期后刷新token并重新发起请求

1.Token缓存(SharedPreferences)

2.判断是否失效

3.Observable

4.retryWhen

5.map

6.TokenLoader

7.场景模拟

10.屏幕旋转导致Activity重建时恢复任务

1.Android屏幕旋转处理方式

(1)禁止旋转

(2)旋转后恢复现场(官方推荐)

(3)手工处理旋转(官方不推荐)

2.RxJava实现Activity重建时恢复任务

(1)定义一套Fragment与Activity通讯的接口

(2)定义任务Fragment:WorkerFragment

(3)模拟一个旋转屏幕需要销毁重建的Activity(实现Iholder接口)

11.网络重连自动请求

1.定位模块

2.网络状态模块

3.整合上述两个触发条件,并实现网络请求逻辑

12.模拟60秒发送验证码


1.计算一段时间内的平均值

应用场景:不定期产生一些数值,需要计算固定一段时间内的平均值

(1)Observable 模拟了按随机时间发送数据值的场景。

(2)buffer(200,TimeUnit.MILLISECONDS) 把一段时间(200s)发送的数据值收集起来,生成一个List发送给Consumer,Consumer收到后计算出平均值。

Observable.create(new ObservableOnSubscribe<Integer>() {

    @Override
    public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Throwable {
        e.onNext(1);
        Thread.sleep(50);
        e.onNext(2);
        Thread.sleep(90);
        e.onNext(3);
        Thread.sleep(60);
        e.onNext(4);
        Thread.sleep(70);
    }
}).buffer(200,TimeUnit.MILLISECONDS)
        .subscribe(new Consumer<List<Integer>>() {
    @Override
    public void accept(List<Integer> integers) throws Throwable {
        if(integers.size() <= 0) {
            return;
        }
        int result = 0;
        for(Integer i : integers) {
            result += i;
        }
        Log.i("avg","avg = " + result / integers.size());
    }
});

2.优化搜索联想功能

搜索联想:每当搜索框中输入的key发生变化,随即返回相应的搜索结果并展示。

优化项:

(1)连续输入引发的不必要请求。用户输入abc,App会连续发起三次关键字搜索request:a,ab,abc

(2)连续request,返回乱序。(1)场景下无法保证response按照a,ab,abc顺序返回,导致实际搜索结果错误。

解决方案:

按照上述场景,实际需要请求的是abc,即输入的最后结果key!

(1)debounce操作符,当输入框发生变化时,不会立刻将事件发送给下游,而是等待500ms,如果在这段事件内,输入框没有发生变化,那么才发送该事件;反之,则在收到新的关键词后,继续等待500ms。

(2)filter操作符,只有关键词的长度大于0时才发送事件给Observer。

(3)switchMap操作符,即便当ab、abc都触发request,即使ab的结果返回了,也不会发送给Observer,从而避免了出现前面介绍的搜索词和联想结果不匹配的问题。

RxTextView.textChanges(editText)

        // 表示延时多少秒后执行,当你敲完字之后停下来的半秒就会执行下面语句
        .debounce(500, TimeUnit.MILLISECONDS)
        // 只有关键词的长度大于0时才发送事件给Observer
        .filter(new Predicate<CharSequence>() {
            @Override
            public boolean test(CharSequence charSequence) throws Exception {
                return charSequence != null && charSequence.length() > 0;
            }
        })
        // 数据转换 flatMap: 当同时多个数据请求访问的时候,前面的网络数据会覆盖后面的网络数据
        // 数据转换 switchMap: 当同时多个网络请求访问的时候,会以最后一个发送请求为准,前面网络数据会被最后一个覆盖
        .switchMap(new Function<CharSequence, ObservableSource<List<String>>>() {
            @Override
            public ObservableSource<List<String>> apply(
                    @NonNull CharSequence charSequence) throws Exception {
                // 网络请求操作,获取我们需要的数据
                List<String> list = new ArrayList<String>();
                list.add("2017");
                list.add("2018");
                return Observable.just(list);
            }
        })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe();

3.优化多次重复点击

场景:用户多次点击,造成重复请求。

RxView.clicks(button).throttleFirst(1, TimeUnit.SECONDS)

        .subscribe(new Observer<Object>() {
            @Override
            public void onSubscribe(Disposable d) {}
            @Override
            public void onNext(Object o) {
                Log.i("JAVA", "onClick");
            }
            @Override
            public void onError(Throwable e) {}
            @Override
            public void onComplete() {}
        });

4.RxJava + Retrofit + OkHttp

参见Retrofit相关资料

5.页面轮询请求

interval操作符:固定延时请求,每隔n秒请求一次。

Observable

        // 每间隔10s轮询一次
        .interval(10, TimeUnit.SECONDS)
        // 防止由于Activity已经销毁,但Observable依然在发送数据导致的内存泄漏
        .takeWhile(new Predicate<Long>() {
            @Override
            public boolean test(Long aLong) throws Exception {
                // 用于中断interval的发送,当Activity在 onDestory / onPause等方法 设置 needRefresh 为 false时,Observable将停止发送数据
                return needReFresh;
            }
        }).subscribeOn(Schedulers.computation())
        .observeOn(Schedulers.io())
        .map(new Function<Long, String>() {
            @Override
            public String apply(Long aLong) throws Exception {
                // 发送网络请求,获取结果
                return null;
            }
        }).observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {}
            @Override
            public void onNext(String string) {
                // update UI
            }
            @Override
            public void onError(Throwable e) {}
            @Override
            public void onComplete() {}
        });

扩展:也可以用repeat,repeatWhen实现。

6.接口重新请求(retry)

应用场景:

1.规定重试次数

2.规定重试条件,什么条件下需要重试,什么条件下不需要重试,重试的时间、方式等

解决方案:

retryWhen + flatMap

// 用于在UI线程结束前,清空订阅对象,防止内存溢出

final CompositeDisposable mCompositeDisposable = new CompositeDisposable();
final String ERROR_CODE1 = "error_code1";
final String ERROR_CODE2 = "error_code2";
final int apiMaxRetryCount = 4;
Observable.create(new ObservableOnSubscribe<String>() {
    @Override
    public void subscribe(@NonNull ObservableEmitter<String> e) throws Throwable {
        // doNetWork
        String result = doNetWork();
        // 模拟失败
        if (null == result) {
            e.onError(new Throwable(ERROR_CODE1));
        } else {
            // 模拟成功
            e.onNext(result);
            e.onComplete();
        }
    }
}).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
    private int retryCount = -1;
    private long retryWaitingTime = 0;
    @Override
    public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Throwable {
        // 根据Throwable类型,实现不同的重试策略(是否重试,重试延时,重试次数)
        return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
            @Override
            public ObservableSource<?> apply(Throwable throwable) throws Throwable {
                // throwable为空属于异常情况,不做重试,直接结束
                if (null == throwable) {
                    return Observable.error((throwable));
                }
                switch (throwable.getMessage()) {
                    case ERROR_CODE1:
                        // ERROR_CODE1,延时1000s后重试
                        retryWaitingTime = 1000;
                        break;
                    case ERROR_CODE2:
                        // ERROR_CODE2,延时2000s后重试
                        retryWaitingTime = 2000;
                        break;
                    default:
                        break;
                }
                retryCount++;
                // 控制重试次数小于允许的最大重试次数;否则返回error,调用Observer的onError,结束本次请求
                return retryWaitingTime > -1 && retryCount < apiMaxRetryCount ?
                        Observable.timer(retryWaitingTime, TimeUnit.SECONDS)
                        : Observable.error(throwable);
            }
        });
    }
}).subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {
                mCompositeDisposable.add(d);
            }
            @Override
            public void onNext(@NonNull String s) {
                // 接收正常请求结果,更新UI
            }
            @Override
            public void onError(@NonNull Throwable e) {
                // 处理异常情况
            }
            @Override
            public void onComplete() {}
        });

7.本地验证输入有效性

模拟场景:用户名、密码登录。长度要求分别为 2-8 和 4-16。如果两者都正确,“登录”恢复可点击,否则置灰不可点击

final CompositeDisposable compositeDisposable = new CompositeDisposable();

PublishSubject<String> userNamePublisher = PublishSubject.create();
PublishSubject<String> userPasswordPublisher = PublishSubject.create();
userNameEdit.addTextChangedListener(new EditTextMonitor(userNamePublisher));
passwordEdit.addTextChangedListener(new EditTextMonitor(userPasswordPublisher));
Observable.combineLatest(userNamePublisher, userPasswordPublisher, new BiFunction<String, String, Boolean>() {
    @Override
    public Boolean apply(String name, String password) {
        if (null == name || null == password) {
            return false;
        }
        return name.length() >= 2 && name.length() <= 8 && password.length() >= 4 && password.length() <= 16;
    }
}).subscribe(new Observer<Boolean>() {
    @Override
    public void onSubscribe(@NonNull Disposable d) {
        compositeDisposable.add(d);
    }
    @Override
    public void onNext(@NonNull Boolean aBoolean) {

// update UI
    }
    @Override
    public void onError(@NonNull Throwable e) { }
    @Override
    public void onComplete() { }
});

首先创建两个PublishSubject,用于对用户名、密码输入框的订阅,通过重写TextWatcher,实现在afterTextChanged(Editable editable) 触发时,使用Pub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值