Rxjava操作符(defer,compose,retryWhen)

RxJava compose 操作符使用 复用线程调度链,减少重复代码。 阅读详情

上一篇博客讲了Retrofit的简单使用,应该看过的都基本了解我们公司这个服务器请求网络数据的流程,我来简单梳理一下:

  • 第一次登录,创建cookiejar,请求服务器数据,保存accesstoken到本地
  • 请求其他网络数据,使用已存在的cookiejar,传入本地保存的accesstoken
  • 基本上是这样,但是在实际操作中,用户可能登录之后,过了很久才去请求其他数据,这时候cookie已经失效就需要有重试机制。

  • 请求接口数据
  • 如果服务器返回的状态码是登录超时,则需要在代码中实现自动重新登录,并保存新的cookie和新的accesstoken
  • 使用新的accesstoken和cookie再次请求该接口数据
  • 如果返回状态码是成功,直接进行后续操作
  • 需求已经梳理清楚了,如果要用okhttp来实现以上的这个功能,或者说用异步任务/Handler 来实现网络请求。
    重试这一步就显得异常艰难,直接导致了代码重用困难的问题。
    即使我们使用异步任务/Handler 实现了以上流程,我们也只能在每次请求的时候复制这部分的处理代码,因为对于重试这部分,每次请求的代码都不一样,难以抽取成通用代码。
    当然okhttp有重试机制,但是如果要通过判断请求结果状态,然后再执行一定操作,再重试, 用okhttp实现起来也是非常困难的。
    如果再增加一个重试次数限制,那可想而知,代码量会有多大了。

    但是,是用Rx我们能够轻松地对Obserable进行变换,判断,处理等等,前提是你得熟练Rx。Rx也提供了对变换的封装。我们能够将一系列相同的变换封装成一个Transform,这涉及到compose操作符。
    接下来我先简单介绍一下compose、defer、retryWhen这几个操作符。

    compose

    compose其实就是对Obserable进行一系列的变换。
    举个最简单的例子,基本上在网上搜索compose都能够搜索到这个例子。
    经常使用RxAndroid的小伙伴们应该知道,每次请求网络的时候必加的代码就是,线程切换。

    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())

    这两句代码其实我也是深恶痛绝的, 每次都写错,不过没关系,我们可以通过compose来简单封装一下这两句代码。

    <T> Transformer<T, T> applySchedulers() {
     return new Transformer<T, T>() {
       @Override
       public Observable<T> call(Observable<T> observable) {
         return observable.subscribeOn(Schedulers.io())
             .observeOn(AndroidSchedulers.mainThread());
       }
     };
    }

    写一个applySchedulers()方法,内部实现这个变换。
    使用的时候很简单。

    .compose(applySchedulers())

    这一句就实现了线程切换,而且不容易出错。这是compose最基本的用法,看了上面的例子,对compose应该有了基本的了解。

    defer

    defer其实就是每次产生一个新的Obserable。
    在我们创建Obserable的时候通常都会传递一些参数,有时候这个参数会在程序运行的过程中发生改变,当再次调用这个方法时,要使用最新的参数值,而不是原来的参数值。
    这么描述可能还是不知道它到底有什么用,待会会在例子中用上,相信你一定能对它有个比较深刻的理解。

    retryWhen

    retryWhen其实就是在某个条件达成的时候,重新调用Obserable中的方法。

    接下来我们实现一下这个cookie or token过期,自动重新登录,并重新请求数据,并最终封装成通用的方法。

    api.getCompanyInfo("GetCompanyInfo", Dic.key, Dic.accessToken)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .flatMap(new Func1<RequestResult<List<CompanyInfo>>, Observable<? extends RequestResult<List<CompanyInfo>>>>() {
                @Override
                public Observable<? extends RequestResult<List<CompanyInfo>>> call(RequestResult<List<CompanyInfo>> listRequestResult) {
                    if (listRequestResult.getErrcode() == -1) {
                        System.out.println("cookie失效");
                        //如果请求到的数据 错误码是 -1,抛出一个运行时异常错误。
                        return Observable.error(new RuntimeException("cookieError"));
                    }
                    //否则,说明数据正常获取到了,直接将数据传递下去。
                    return Observable.just(listRequestResult);
                }
            })
            //重新发起请求,当...发生某个错误的时候。
            // 在上一个flatmap中我们抛出的是 运行时异常,内容是"cookieError"
            .retryWhen(observable -> {
                //由于接收到的是一个Obserable.error对象,我们要对这个错误进行处理,需要flatMap一下。
                return observable.flatMap(new Func1<Throwable, Observable<?>>() {
                    @Override
                    public Observable<?> call(Throwable throwable) {
                        //当发生这个cookieError错误的时候,实现自动登录
                        if (throwable instanceof RuntimeException) {
                            return api.UserLogin("action","key","fancy","123456")
                                    .subscribeOn(Schedulers.io())
                                    .observeOn(AndroidSchedulers.mainThread())
                                    .flatMap(new Func1<RequestResult<String>, Observable<? extends Integer>>() {
                                        @Override
                                        public Observable<? extends Integer> call(RequestResult<String> stringRequestResult) {
                                            //自动登录后,把新的token保存下来。
                                            Dic.accessToken = stringRequestResult.getAccesstoken();
                                            //登录后,不需要为下一个步骤传递任何参数,所以直接使用 just就可以了。
                                            return Observable.just(1);
                                        }
                                    });
                        }
                        //发生其他错误,可以进行其他处理,这里就没做处理了,简单抛出一个非法参数错误。
                        // 最终会在 subscribe中的onError方法中得到统一处理
                        return Observable.error(new IllegalArgumentException("没救了"));
                    }
                });
            })
            .subscribe(new ServerSubscriber<RequestResult<List<CompanyInfo>>>() {
                @Override
                public void onNext(RequestResult<List<CompanyInfo>> listRequestResult) {
                    //输出结果
                    System.out.println(listRequestResult);
                }
            });

    代码虽然很长,但每一个步骤都非常清晰。
    如果替换成lambda可能看上去会好看一点。

    代码写好后,尝试着测试一下,由于写了线程切换(android线程),所以只能在android中测试了。 如果希望在junit中测试,删掉所有的线程切换代码即可。

    跑一遍代码,发现cookie始终都是失效,并且会不断地重试。这是因为我们没对重试次数进行控制,这个次数控制在以上代码中实现非常简单, 加一个 次数技术就可以了,就不详细说了。

    现在我们看看是什么原因导致cookie一直失效呢,首先我们用的相同的cookieJar,应该不会不一样才对,并且重新登录后把新的accesstoken保存到了内存中。

    其实,之所以失效是因为对Obserable的工作机制没有理解透彻导致的。

    在创建一个Obserable的时候,参数的内容,固定的步骤就已经决定好了。
    即使在过程中参数发生了改变,retry的时候,还是使用原来的值去请求的。如何验证呢?同样用上篇博客中提到的HttpLoggingInterceptor 就可以验证是不是这样了。日志我就不贴了,验证结果就是参数并没有发生改变。

    所以,我们需要每次重试的时候,产生一个新的Obserable。这时候我们可以借助defer操作符来实现,每次调用都是一个新的Obserable。
    把请求接口的Obserable用Obserable.defer包裹一下即可。

    Observable.defer(new Func0<Observable<RequestResult<List<CompanyInfo>>>>() {
                @Override
                public Observable<RequestResult<List<CompanyInfo>>> call() {
                    return api.getCompanyInfo("GetCompanyInfo", Dic.key, Dic.accessToken);
                }
            })

    用lambda格式化一下

    Observable.defer(() -> api.getCompanyInfo("GetCompanyInfo", Dic.key, Dic.accessToken))

    是不是非常简单。
    用defer包裹之后再测试,就能够正常访问到接口的结果了。并且会在控制台输出一次cookie失效。说明成功调用了retry中的方法。

    到这里,基本上解决这个问题的步骤就差不多了,但是,我们还需要对这个重试操作进行封装一下,不用每次都复制这么长的代码,多累啊。

    最开始我想到的封装,就是封装两次,就把两个Func1封装起来就行了。
    最终调用方法如下:

    Observable.defer(() -> api.getCompanyInfo("GetCompanyInfo", Dic.key, Dic.accessToken))
                    .flatMap(new CookieDeal<List<CompanyInfo>>())
                    .retryWhen(new ReLoginDeal().getFunc1(api))

    看上去还是封装的比较简单了。但是每次new CookieDeal的时候都要手动输入参数类型,这太不爽了,而且要写两个步骤,看上去一点也不优雅。

    最后我通过了解compose这个操作符的作用,对这个重试机制进行了终极封装。
    使用方法如下:

    .compose(ReLoginDeal.relogin())

    一句话,是不是不能再简单,关键是不用再手动打一个类型上去,测试的时候很方便。
    贴一下relogin()方法的代码吧:

    public static <T> Observable.Transformer<RequestResult<T>, RequestResult<T>> relogin() {
            return observable -> observable
                    .flatMap(tRequestResult -> {
                        if (tRequestResult.getErrcode() == -1) {
                            //重新登录。
                            System.out.println("cookie 失效");
                            return Observable.error(new RuntimeException("cookieError"));
                        }
                        return Observable.just(tRequestResult);
                    })
                    .retryWhen((Func1<Observable<? extends Throwable>, Observable<?>>) observable1 -> observable1.flatMap(new Func1<Throwable, Observable<?>>() {
                        @Override
                        public Observable<?> call(Throwable throwable) {
                            if (throwable instanceof RuntimeException) {
                                    return RetrofitUtil.getUserApi().UserLogin("userLogin",
                                            MD5.hexdigest("key"),
                                            MyEncode.encode("fancy"),
                                            MyEncode.encode("123456"))
                                            .flatMap(stringRequestResult -> {
    
                                                Dic.accessToken = stringRequestResult.getAccesstoken();
                                                return Observable.just(1);
                                            });
                            }
                            return Observable.error(new IllegalArgumentException("没救了"));
                        }
                    }));
        }

    这个重试机制中登录的用户名密码是写死的,可以通过参数传递做成一个动态的。

    基本上封装就结束了,细节部分再自行优化一下就ok啦。

    对本文内容有任何疑问欢迎加群讨论:283272067

全志V3S开发板实战:从零构建嵌入式Linux系统 本文提供了一份针对全志V3S开发板(如LicheePi Zero)的零基础教程,详细指导如何从零开始构建嵌入式Linux系统。内容涵盖编译环境搭建、交叉工具链配置、U-Boot引导程序编译与烧录、Linux内核定制与编译,以及使用Buildroot构建根文件系统,最终实现系统成功启动,帮助开发者深入理解嵌入式系统构建全流程。 阅读详情

相关推荐

PX4环境git submodule update --init --recursive失败的解决办法

最近开始搭建PX4环境,搭建是需要从github下载工程,然后使用语句git submodule update --init --recursive更新工程子模块,但往往由于网络原因这一步需要很久,甚至会失败。因为github是外网,在上面下载代码会受到很大限制,往往会出现下面的问题: 即无法连接到github,查阅了相关资料后,我发现了解决更新子模块失败的方法: 1.打开你下载的PX4源码,即Firmware文件夹,找到其中的.gitmodules,如下图: 2…gitmodules有以上格式,path

qq_36903625的博客 1万+

RxJava(六)retryWhen 操作符实现错误重试机制

RxJava系列文章目录导读: 一、RxJava create操作符的用法和源码分析 二、RxJava map操作符用法详解 三、RxJava flatMap操作符用法详解 四、RxJava concatMap操作符用法详解 五、RxJava onErrorResumeNext操作符实现app与服务器间token机制 六、RxJava retryWhen操作符...

Chiclaim 3万+

DaVinci Configurator BswM模块配置

BswM是AUTOSAR(Automotive Open System Architecture)系统架构中的模块之一,它是基础软件模块(BSW)之一。BswM全称为Basic Software Module Manager(基础软件模块管理器),它负责管理和协调其他BSW模块的行为和交互,以实现整个系统的功能。BswM可以被看作是一个状态机,通过监控系统的状态和事件触发,来控制其他BSW模块的运行状态和互相配合,以达到整个系统的预期行为。

qq_59258431的博客 3945

rxjava : 错误处理操作符 (1):retry 、 retryUntil 、 retryWhen

参考:[Android开发] RxJava2之路七 - 错误处理操作符例子Demo retry操作符 重试的意思,拦截到错误,然后让 被观察者重新发射数据。Throwable和Exception都额可以拦截 它有五种参数方法: retry(): 让被观察者重新发射数据,要是一直错误就一直发送了 retry(BiPredicate): interger是第几次重新发送,Throwable是错...

火星男孩的分享空间 4359

vnpy_ctp源码下载后转变为python可用的处理过程

vnpy_ctp源码下载后在window中编译使用

m0_37967652的博客 3327

英飞凌 AURIX Development Studio工程移植到Hightec的方法

1. 软件简介 AURIX™ Development Studio是英飞凌针对AURIX TC2XX/TC3XX系列单片机推出了一款免费的软件,集成了iLLD库,内置Tasking compiler和Tasking Debugger,但这两个模块只能用于非商业用途,可供学习和试用。iLLD库是英飞凌为TC2XX和TC3XX Tricore系列单片机提供的底层驱动库,有完善的使用手册,和较好的移植性。 Hightec是一款可以用来开发Aurix系列单片机的编译器,有免费版,可商用,集成GCC编译器,可用UDE

weixin_42967006的博客 7136

RxJava2从入门到精通-初级篇

适合所有初中级工程师,从RxJava的每个知识点进行讲解,并在每个知识点中都带有文字说明和代码例子说明。通过这次系统化的学习,您将可以完整的学习到RxJava家族的所有特性和常用的操作符,掌握操作符的使用,掌握RxJava与Rx其他第三方库的使用。可以用三句简单的话进行概括面试加分利器 = 加大录取机会加快工作效率 = 早点下班回家系统化的学习 = 能力全面提升

华南理工大学-大学物理实验-不良导体热导率测量

不良导体热导率测量 引言 导热系数(又叫热导率)是反映材料热性能的重要物理量。热传导是热交换的三种(热传导、对流和辐射)基本形式之一,是工程热物理、材料科学、固体物理及能源、环保等各个研究领域的课题。材料的导热机理在很大程度上取决于它的微观结构,热量的传递依靠原子、分子围绕平衡位置的振动以及自由电子的迁移。在金属中电子流起支配作用,在绝缘体和大部分半导体中则以晶格振动起主导作用。因此,某种材料的导热系数不仅与构成材料的物质种类密切相关,而且还与材料的微观结构、温度、压力及杂质含量相联系。在科学实验和工程设计

mohen_777的博客 871

retrywhen实现重连(kotlin)

相信大多数人都在用rxjava+retrofit网络框架,在用着框架的时候我们不难发现,一些复杂,连续请求不再复杂,那么我今天提到的retrywhen操作符作用是什么呐?版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。原文链接:https://blog.youkuaiyun.com/ZyClient/article/details/87251790。

chuyouyinghe的专栏 396

YOLOv8改进 | 主干网络 | 增加网络结构增强小目标检测能力【独家创新——附结构图】

YOLOv8改进,小目标检测,yolov8

kay_545 6019

Qt入门教程【环境搭建篇】Qt6+Mingw+QMake+QtCreator

Qt Windows平台安装

小鱼酱的博客 2万+

【Android面试】RxJava专题

Android Rxjava面试专题

studyForMokey的博客 378

PY32F406K1CU6 FLASH模拟EEPROM

摘要:本文介绍了基于STM32 FLASH模拟EEPROM的实现方法。头文件flash.h定义了FLASH页大小(256字节)、用户存储区域(4页)及地址范围,提供了等待周期配置和地址校验宏,声明了EEPROM初始化、读写操作等函数接口。源文件flash.c实现了具体功能:配置FLASH等待周期(144MHz需3WS)、页读写擦除操作,以及EEPROM初始化(解锁-擦除-验证-锁定)和缓冲区读写功能。其中写操作采用"读页→更新→擦除→编程"的流程,确保数据安全写入。该方案适用于需要非易失

sam的博客 194

Rxjava操作符compose()

原文链接:http://blog.danlew.net/2015/03/02/dont-break-the-chain/在RXJava中一种比较nice的思想是能够通过一系列的操作符看到数据是如何转换的:Observable.from(someSource) .map(data -> manipulate(data)) .subscribeOn(Schedulers.io())

Dream It Possible 3万+

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

java 指数退避算法_对RxJava中.repeatWhen()和.retryWhen()操作符的思考

译者: 小鄧子状态: 完成译者注:为了方便因Lambda(译文)还不够了解的同学进行阅读,本篇译文替换了原文中全部Lambda表达式。第一次见到.repeatWhen()和.retryWhen()这两个操作符的时候就非常困惑了。不得不说,它们绝对是“最令人困惑弹珠图”的有力角逐者。然而它们都是非常有用的操作符:允许你有条件的重新订阅已经结束的Observable。我最近研究了它们的工作...

weixin_42363662的博客 633

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

RxJava retryWhen 异常业务处理总结

通过对rxjava retryWhen 的封装和使用,可以优化异常处理,在统一异常处理中有较为突出的效果

cxmfzu的博客 406

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

RxJava(六) retryWhen操作符实现错误重试机制

业务需求 当我们在app里发起网络请求时,可能会因为各种问题导致失败。如何利用RxJava来实现出现错误后重试若干次,并且可以设定重试的时间间隔。 具体实现 网络请求使用Retrofit来做,还是使用上篇博客中的请求用户信息接口 @GET("/userinfo?noToken=1") Observable<Response> getUserInfoNoToken(...

android开发笔记 1717

RxJava操作符 ~ defer

RxJava的defer()操作符作以确保Observable代码在被订阅后才执行(而不是创建后立即执行) @Test public void defer() { Disposable disposable = Observable.defer(new Callable<ObservableSource<?>>() { @Override public ObservableSource<?> ca.

南柯好萌的博客 3210

网络求情重试-RetryWhen

项目 vaue 你 123 电脑 太瘦 哈哈 234 一个具有注脚的文本。1 一个具2 asdadfadsf public void test 注脚的解释 ↩︎ 注脚的解释 ↩︎

u010741032的博客 464

FlatMap与Compose操作符

RxJava中的`compose`方法是一种**组合操作符**,它可以将一系列的RxJava操作符组合在一起,形成一个新的Observable。`compose`方法的定义如下:

xiaokangss的博客 532

RxJava系列10:理解rxjava中的compose

理解rxjava中的compose 一.compose基本概念及使用 引入: Obsrvable.just().map.map.map.filter.subscribeOn.observeOn.subscribe() 如果我们相对多个流重复利用一系列操作符该怎么办? 一种方式是,提取一个工具方法,把复用的地方抽取成一个方法applySchedulers。 applySch...

有心好书 6524

RxJava之defer操作符

https://blog.youkuaiyun.com/zhuxuliao/article/details/51542809 Defer操作符 1. 作用 defer 操作符与create、just、from等操作符一样,是创建类操作符,不过所有与该操作符相关的数据都是在订阅是才生效的。 2. 用法: i = 12; //注意此处defer的参数是Func0,而不是OnSubscrie ...

lppl010_的专栏 547

RxJava(十一)defer 操作符实现代码支持链式调用

RxJava系列文章目录导读: 一、RxJava create操作符的用法和源码分析 二、RxJava map操作符用法详解 三、RxJava flatMap操作符用法详解 四、RxJava concatMap操作符用法详解 五、RxJava onErrorResumeNext操作符实现app与服务器间token机制 六、RxJava retryWhen操作符...

Chiclaim 1万+

RxJava系列:理解rxjava中的compose操作符

理解rxjava中的compose 一.compose基本概念及使用 引入: Obsrvable.just().map.map.map.filter.subscribeOn.observeOn.subscribe() 如果我们相对多个流重复利用一系列操作符该怎么办? 一种方式是,提取一个工具方法,把复用的地方抽取成一个方法applySchedulers。 applySch...

cmyperson的博客 2257

RX操作符之错误处理(catch[onErrorReturn、onErrorResumeNext、onExceptionResumeNext]、retry、retryWhen)

一、catch 从onError通知中恢复发射数据。Catch操作符拦截原始Observable的onError通知,将它替换为其它的数据项或数据序列,让产生的Observable能够正常终止或者根本不终止。 1.onErrorReturn 让Observable遇到错误时发射一个特殊的项并且正常终止。 Observable observable = Observab

nicolelili1的专栏 1万+

RxJava----操作符:错误处理

这些操作符用于从错误通知中恢复 catch Catch类似于java中的try/catch,当错误发生的时候,可以拦截对onError的调用,让Observable不会因为错误的产生而终止。在Rxjava中,将这个操作符实现为3个操作符,分别是: onErrorReturn 当发生错误的时候,让Observable发射一个预先定义好的数据并正常地终止 Observable<String> values =Observable.create(new Observa.

chuyouyinghe的专栏 770
上一篇: Retrofit、Okhttp使用小记(cookie,accesstoken,POST)
下一篇: Realm使用小记
浅浅清风
博客等级 码龄12年 42粉丝 31原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值