一.RxJava_rxjava 防抖-程序员宅基地

技术标签: android  开源框架  rxjava  

1.RxJava使用场景

RxJava核心思想

Rx思维:响应式编程,从起点到终点,中途不能断掉,并且可以在中途添加拦截.
生活中的例子:
起点(分发事件,我饿了)->下楼->去餐厅->点餐->终点(吃饭,消费事件)
程序中的例子:
起点(分发事件,点击登录)->登录API->请求服务器->获取响应码->终点(更新UI登录成功,消费事件)

总结:
有一个起点和一个终点,起点开始流向我们的“事件”,把事件流向终点,只不过在流向终点的过程中,可以增加拦截,拦截时可以对"事件进行改变",终点只关心他的上一个拦截.

Retrofit配合RxJava使用

Retrofit是对OkHttp网络请求框架的封装,我们将从OkHttp请求到数据的响应给到RxJava进行处理.

防抖

作用:防止重复操作.

举例1:
防止用户一直去请求获取验证码接口,黑客攻击1s内请求100次获取验证码接口;但是我们可以利用防抖思想,对其进行拦截,让他100次只做第一次处理,甚至一天之内最多只能请求5次获取验证码接口.
举例2:
我们点击某个按钮,可能存在重复点击的情况,我们可以利用RxBinding来防止重复点击做重复网络请求.

代码举例:

//TODO 5s内点击按钮只有第1次生效弹出Toast,超过5s后点击按钮才会第二次弹出Toast
RxView.clicks(findViewById(R.id.tv_fangdou))
      .throttleFirst(5, TimeUnit.SECONDS)//表示5s内只有第一次点击生效
      .subscribe(new Consumer<Object>() {
    
         @Override
         public void accept(Object o) throws Exception {
    
            Toast.makeText(UseActivity.this, "5s内只有第1次点击生效了", Toast.LENGTH_SHORT).show();
         }
      });
网络嵌套

先请求主数据,然后在根据主数据中的某个字段去请求子数据.
比如:我们先获取到某个用户的朋友列表,然后根据某个朋友的ID(如:张三)去查询朋友的信息.
解决方案:
可以采用flatMap这种方式去做处理,可以实现多个嵌套的网络请求在同一层级上面展示,不会像多层嵌套那样不易阅读.

doOnNext运用

频繁的在主线程与子线程之间切换来完成我们的业务.
举例:
银行项目存在频繁在主线程与子线程之间切换,可以采用doOnNext这种方式来解决.

2.RxJava模式与原理

标准观察者与RxJava观察者

标准观察者:
一个被观察者(Observable),可以有多个观察者(Observer),被观察者发生改变,所有订阅了他的观察者都能收到这个变化消息.
举例:
移动公司给所有用户发送一条短信,移动公司就作为被观察者,而所有的用户就作为观察者.

RxJava观察者流程:

  • 创建Observable
  • 创建Observer
  • 使用subscribe()订阅

分析RxJava观察者流程时,不按照上面的步骤来:

  1. 查看Observer源码
    • 定义了Observer接口的方法,比如:onSubscribe、onNext、onError、onComplete
    • 然后在使用的时候,直接创建自定义观察者,将new新建的Observer传入作为参数,重写实现方法.
  2. 了解Observable创建过程,分析源码
    • 调用create()方法会创建ObservableCreate对象
    • 将自定义(ObservableOnSubscribe)source资源传入ObservableCreate对象,作为一个参数
  3. 了解subscribe订阅过程,分析源码
    • 在订阅的过程中,首先执行观察者中onSubscribe方法,然后执行onNext/onError,最后执行onComplete方法
    • subscribe方法传入的参数是观察者Observer,调用者是被观察者Observable,有一个中间层发射器ObservableEmitter
    • 在执行subscribe方法时,最终会调用到Observable的实现类ObservableCreate的subscribeActual方法

标准观察者设计模式和RxJava观察者设计模式比较:

  • 在标准观察者设计模式中,是一个被观察者,对应多个观察者,并且被观察者发出改变通知后,所有的观察者才能观察到;耦合度高.
  • 在RxJava观察者设计模式中,是多个被观察者,一个观察者,并且需要起点和终点在订阅一次后,才发出改变通知,终点观察者才能观察到;耦合度低,也叫发布/订阅模式,也可以叫作观察者模式.

扩展知识:
RxJavaPlugins.setOnObservableAssembly()可以实现Hook,全局监听整个项目RxJava执行了哪些Observable;RxJavaPlugins就是一个用来做全局监听的工具类,里面包含了多种功能.

map变换操作符原理

map是用来做类型转换的,比如:将String类型转换成Integer类型,也可以将一个对象映射成另外一个对象.
代码举例:

.map(new Function<String, Integer>() {
    //通过map中传入Function,将String转换成Integer类型
   @Override
   public Integer apply(String s) throws Exception {
    
         //返回Integer类型
         return 9527;
   }
})

洋葱模型:

  1. 观察者(终点):
    new Observer作为参数传入订阅方法subscribe
  2. 订阅(subscribe(observer)):
    • 这个方法中会调用subscribeActual(observer)方法,由于加入了map拦截,所以由map方法返回的ObservableMap对象来调用subscribeActual方法.
    • ObservableMap.subscribeActual(observer)方法中做了哪些事情:
      public void subscribeActual(Observer<? super U> t) {
              
         //MapObserver作为Observer的包装(封装/包裹),该类持有了Observer成员变量actual
         //通过MapObserver<T, U>对类型进行转换,将T类型转换成U类型
         //这里的source是上一层传递过来的对象,而MapObserver是封装的是下一层的包裹(Observer)t
        source.subscribe(new MapObserver<T, U>(t, function));
      }
      
    • 第一次包装,采用MapObserver进行包装,这里的参数t就是观察者Observer.
  3. map:
    作用:卡片拦截,在被观察者与观察者之间添加拦截,可以进行类型转换.
    流程分析:
    • 该方法返回包装类ObservableMap<T, U>,这个类可以将T类型转换成U类型并返回;
    • 最终体现在map方法参数Function类的apply方法中,将转换后的U类型返回.
  4. map(多重拦截):
    • 对上一次包裹Observer进行再次包装,采用的MapObserver进行包装.这里的参数t就是上一次包装生成的观察者Observer.
    • 最终由Observable的实现类ObservableCreate来调用该方法;
    • ObservableCreate.subscribeActual(observer)方法中做了哪些事情:
      protected void subscribeActual(Observer<? super T> observer) {
              
         //1.包装观察者,将观察者作为参数传入创建的发射器对象Emitter
         //由于我们做过拦截,所以这里传入的是包装后的Observer
         CreateEmitter<T> parent = new CreateEmitter<T>(observer);
         //2.调用onSubscribe方法,所以这个方法早于我们的执行流程
         observer.onSubscribe(parent);
      
         try {
              
            //3.自定义source开始订阅,并将发射器作为参数传入;
            //这个方法就会执行到我们自定义ObservableOnSubscribe的subscribe方法,这里就会去拆包裹
            source.subscribe(parent);
         } catch (Throwable ex) {
              
            Exceptions.throwIfFatal(ex);
            //如果报错走最外层包裹的onError方法
            parent.onError(ex);
         }
      }
      
  5. create:
    创建ObservableCreate对象并返回,并将自定义ObservableOnSubscribe作为source参数传入.
    • 最后一次包装,采用CreateEmitter进行包装,代码如下:
      CreateEmitter<T> parent = new CreateEmitter<T>(observer);
  6. 自定义source:
    在自定义ObservableOnSubscribesubscribe方法中,可以去执行onNext方法.通过查看源码流程走向,调用该方法后,就会依次调用每一个Observable实现类中内部包装类的onNext方法,最终调用到我们通过new创建的Observer中的onNext方法.

总结:

  1. 首先,RxJava的执行流程是从上往下的,依次创建Observable的实现类,最终调用订阅subscribe方法;
  2. 其次,调用完订阅subscribe方法后,就开始从下往上依次对观察者Observer封装包裹.
    说明:(source.subscribe(包装类(observer)),是封装包裹发起者)
    map方法的包装类是MapObserver,返回的实现类是ObservableMap对象;
    create方法的包装类是CreateEmitter,返回的实现类是ObservableCreate对象;
  3. 最后,我们在自定义source(ObservableOnSubscribe)的回调方法subscribe方法中,执行包装类的onNext或onComplete方法时,就会从上往下,依次从外向内开始拆包裹.
    说明:(包装类.onNext和onSubscribe是拆包裹发起者)
    依次执行当前包裹中封装的Observer的onNext或onComplete方法,最终执行到我们自定义Observer的onNext或onComplete方法,至此完成整个流程.

RxJava中map流程图如下:
请添加图片描述

背压

消费的速度跟不上生产的速度时,就存在背压的问题,我们可以采用Flowable替换Observable来解决背压的问题.

3.RxJava原理与自定义操作符

线程切换原理
  • subscribeOn() 给上面的代码分配线程
    Schedulers.io()最终会通过线程池来进行管理,因此后面执行的任务都是在子线程中进行.
    执行步骤:(Schedulers.io() == IoScheduler(持有线程池))
    • Schedulers.io()->(Scheduler)Schedulers.IO->new IOTask()->IOTask.run()->IoHolder.DEFAULT->new IoScheduler()->IoScheduler.start()->new CachedWorkerPool ->CachedWorkerPool类持有线程池变量:ScheduledExecutorService evictorService
      //构造函数中将线程池变量evictor赋值给成员变量evictorService
      CachedWorkerPool(long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) {
              
               //省略无关代码
               ScheduledExecutorService evictor = null;
               if (unit != null) {
              
                  evictor = Executors.newScheduledThreadPool(1, EVICTOR_THREAD_FACTORY);
               }
               //创建线程池并赋值给evictorService成员变量
               evictorService = evictor;
         }
      
  • observeOn() 给下面的代码分配线程
    AndroidSchedulers.mainThread()最终是通过Handler来完成子线程到主线程的切换,因此后面的代码可以更新UI.
    执行步骤:(AndroidSchedulers.mainThread() == HandlerScheduler(handler))
    • AndroidSchedulers.mainThread()->(Scheduler)Schedulers.MAIN_THREAD->MainHolder.DEFAULT->new HandlerScheduler(new Handler(Looper.getMainLooper()));
    • 这里传递了主线程的Looper对象给Handler,以确保代码执行在主线程中.

RxJava中onserveOn(AndroidSchedulers.mainThread())流程图:
在这里插入图片描述

扩展知识

观察者Observer的回调方法中会返回一个Disposable对象,我们在页面销毁的时候,需要判断这个对象Disposable是否销毁dispose了,如果没有销毁需要将其销毁.
这样的目的是为了防止内存泄漏,解决在页面销毁的时候,还在执行后面onNextonComplete的逻辑操作的问题.
代码如下:

//使用结果赋值给一个成员变量,在生命周期结束时销毁他
private Disposable mDisposable;

private void doSomething() {
    
   disposable = Observable.create((ObservableOnSubscribe<String>) e -> {
    
      e.onNext("第一步");
      e.onNext("第二步");
      e.onComplete();
   }).subscribe(s -> {
    

   });
}

@Override
protected void onDestroy() {
    
   super.onDestroy();

   //结束生命周期销毁disposable
   if (mDisposable != null && !mDisposable.isDisposed()){
    
      mDisposable.dispose();
   }  
}
自定义RxView操作符

主要是通过自定义Observable继承自Observable,重写subscribeActual(observer)方法,然后在该方法中通过source.subscribe(包装类),将我们封装了下一层Observer包装类传递进来封装包裹;包装类需要实现Disposable,达到可以被中断的目的,同时需要包含下一层包裹Observer变量,以便一层层调用每一层包裹Observer的方法.

总结:

整体实现流程

  1. 通过由上往下一层一层调用Observable的各种方法,创建出Observable的具体实现类.
    比如:通过调用create()方法,会返回ObservableCreate实现类,通过map()方法会返回ObservableMap实现类,通过observeOn()会返回OnservableObserveOn实现类;总之,就是在Observable后面拼接方法名称构成一个对象.
  2. 通过由下往上调用Observable.subscribe(observer)方法封装包裹,接着调用具体实现类中的subscribeActual(observer)方法来完成,最终将封装的包裹通过这种方式传入:
    source.subscribe(new 包装类(observer);
    参数说明:
    source:表示create方法中通过new传入的ObservableOnSubscribe对象
    observer:表示下一层封装的包裹,每一个包裹都是一个包装类,包装类都实现了Observer接口
    subscribe():方法表示ObservableOnSubscribe重写的subscribe(包装类)方法,里面的参数对应每一层的包装类;一般包装类调用onNext方法时,就会调用到包装类中的Observer对象onNext方法,依次达到一层一层往下拆包裹的目的.
  3. 通过由上往下依次调用每个具体实现类.包装类中的onNext方法时,就会直接调用调用observer.onNext方法,这里observer对象就是下一层包裹,因为每一层包裹都实现了Observer接口,以此达到了一层一层往下调用每一层包裹中onNext方法的目的.
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/tangkunTKTK/article/details/130798567

智能推荐

leetcode 172. 阶乘后的零-程序员宅基地

文章浏览阅读63次。题目给定一个整数 n,返回 n! 结果尾数中零的数量。解题思路每个0都是由2 * 5得来的,相当于要求n!分解成质因子后2 * 5的数目,由于n中2的数目肯定是要大于5的数目,所以我们只需要求出n!中5的数目。C++代码class Solution {public: int trailingZeroes(int n) { ...

Day15-【Java SE进阶】IO流(一):File、IO流概述、File文件对象的创建、字节输入输出流FileInputStream FileoutputStream、释放资源。_outputstream释放-程序员宅基地

文章浏览阅读992次,点赞27次,收藏15次。UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。文件字节输入流:每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1。注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。UTF-8字符集:汉字占3个字节,英文、数字占1个字节。GBK字符集:汉字占2个字节,英文、数字占1个字节。GBK规定:汉字的第一个字节的第一位必须是1。_outputstream释放

jeecgboot重新登录_jeecg 登录自动退出-程序员宅基地

文章浏览阅读1.8k次,点赞3次,收藏3次。解决jeecgboot每次登录进去都会弹出请重新登录问题,在utils文件下找到request.js文件注释这段代码即可_jeecg 登录自动退出

数据中心供配电系统负荷计算实例分析-程序员宅基地

文章浏览阅读3.4k次。我国目前普遍采用需要系数法和二项式系数法确定用电设备的负荷,其中需要系数法是国际上普遍采用的确定计算负荷的方法,最为简便;而二项式系数法在确定设备台数较少且各台设备容量差..._数据中心用电负荷统计变压器

HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板_网页设计成品百度网盘-程序员宅基地

文章浏览阅读7k次,点赞4次,收藏46次。HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水平作业_网页设计成品百度网盘

【Jailhouse 文章】Look Mum, no VM Exits_jailhouse sr-iov-程序员宅基地

文章浏览阅读392次。jailhouse 文章翻译,Look Mum, no VM Exits!_jailhouse sr-iov

随便推点

chatgpt赋能python:Python怎么删除文件中的某一行_python 删除文件特定几行-程序员宅基地

文章浏览阅读751次。本文由chatgpt生成,文章没有在chatgpt生成的基础上进行任何的修改。以上只是chatgpt能力的冰山一角。作为通用的Aigc大模型,只是展现它原本的实力。对于颠覆工作方式的ChatGPT,应该选择拥抱而不是抗拒,未来属于“会用”AI的人。AI职场汇报智能办公文案写作效率提升教程 专注于AI+职场+办公方向。下图是课程的整体大纲下图是AI职场汇报智能办公文案写作效率提升教程中用到的ai工具。_python 删除文件特定几行

Java过滤特殊字符的正则表达式_java正则表达式过滤特殊字符-程序员宅基地

文章浏览阅读2.1k次。【代码】Java过滤特殊字符的正则表达式。_java正则表达式过滤特殊字符

CSS中设置背景的7个属性及简写background注意点_background设置背景图片-程序员宅基地

文章浏览阅读5.7k次,点赞4次,收藏17次。css中背景的设置至关重要,也是一个难点,因为属性众多,对应的属性值也比较多,这里详细的列举了背景相关的7个属性及对应的属性值,并附上演示代码,后期要用的话,可以随时查看,那我们坐稳开车了······1: background-color 设置背景颜色2:background-image来设置背景图片- 语法:background-image:url(相对路径);-可以同时为一个元素指定背景颜色和背景图片,这样背景颜色将会作为背景图片的底色,一般情况下设置背景..._background设置背景图片

Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏8次。Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程

PyCharm2021安装教程-程序员宅基地

文章浏览阅读10w+次,点赞653次,收藏3k次。Windows安装pycharm教程新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入下载安装PyCharm1、进入官网PyCharm的下载地址:http://www.jetbrains.com/pycharm/downl_pycharm2021

《跨境电商——速卖通搜索排名规则解析与SEO技术》一一1.1 初识速卖通的搜索引擎...-程序员宅基地

文章浏览阅读835次。本节书摘来自异步社区出版社《跨境电商——速卖通搜索排名规则解析与SEO技术》一书中的第1章,第1.1节,作者: 冯晓宁,更多章节内容可以访问云栖社区“异步社区”公众号查看。1.1 初识速卖通的搜索引擎1.1.1 初识速卖通搜索作为速卖通卖家都应该知道,速卖通经常被视为“国际版的淘宝”。那么请想一下,普通消费者在淘宝网上购买商品的时候,他的行为应该..._跨境电商 速卖通搜索排名规则解析与seo技术 pdf

推荐文章

热门文章

相关标签