Rx思维:响应式编程,从起点到终点,中途不能断掉,并且可以在中途添加拦截.
生活中的例子:
起点(分发事件,我饿了)->下楼->去餐厅->点餐->终点(吃饭,消费事件)
程序中的例子:
起点(分发事件,点击登录)->登录API->请求服务器->获取响应码->终点(更新UI登录成功,消费事件)
总结:
有一个起点和一个终点,起点开始流向我们的“事件”,把事件流向终点,只不过在流向终点的过程中,可以增加拦截,拦截时可以对"事件进行改变",终点只关心他的上一个拦截.
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
这种方式来解决.
标准观察者:
一个被观察者(Observable
),可以有多个观察者(Observer
),被观察者发生改变,所有订阅了他的观察者都能收到这个变化消息.
举例:
移动公司给所有用户发送一条短信,移动公司就作为被观察者,而所有的用户就作为观察者.
RxJava观察者流程:
分析RxJava观察者流程时,不按照上面的步骤来:
标准观察者设计模式和RxJava观察者设计模式比较:
扩展知识:
RxJavaPlugins.setOnObservableAssembly()
可以实现Hook
,全局监听整个项目RxJava
执行了哪些Observable
;RxJavaPlugins
就是一个用来做全局监听的工具类,里面包含了多种功能.
map是用来做类型转换的,比如:将String类型转换成Integer类型,也可以将一个对象映射成另外一个对象.
代码举例:
.map(new Function<String, Integer>() {
//通过map中传入Function,将String转换成Integer类型
@Override
public Integer apply(String s) throws Exception {
//返回Integer类型
return 9527;
}
})
洋葱模型:
观察者
(终点
):new Observer
作为参数传入订阅方法subscribe
订阅(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
.map
:ObservableMap<T, U>
,这个类可以将T
类型转换成U
类型并返回;map
方法参数Function
类的apply
方法中,将转换后的U
类型返回.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);
}
}
create
:ObservableCreate
对象并返回,并将自定义ObservableOnSubscribe
作为source
参数传入.
CreateEmitter
进行包装,代码如下:CreateEmitter<T> parent = new CreateEmitter<T>(observer);
自定义source
:ObservableOnSubscribe
的subscribe
方法中,可以去执行onNext
方法.通过查看源码流程走向,调用该方法后,就会依次调用每一个Observable
实现类中内部包装类的onNext
方法,最终调用到我们通过new
创建的Observer
中的onNext
方法.总结:
- 首先,RxJava的执行流程是从上往下的,依次创建Observable的实现类,最终调用订阅subscribe方法;
- 其次,调用完订阅subscribe方法后,就开始从下往上依次对观察者Observer封装包裹.
说明:(source.subscribe(包装类(observer)),是封装包裹发起者
)
map方法的包装类是MapObserver,返回的实现类是ObservableMap对象;
create方法的包装类是CreateEmitter,返回的实现类是ObservableCreate对象;- 最后,我们在自定义source(
ObservableOnSubscribe
)的回调方法subscribe方法中,执行包装类的onNext或onComplete方法时,就会从上往下,依次从外向内开始拆包裹.
说明:(包装类.onNext和onSubscribe是拆包裹发起者
)
依次执行当前包裹中封装的Observer的onNext或onComplete方法,最终执行到我们自定义Observer的onNext或onComplete方法,至此完成整个流程.
RxJava中map流程图如下:
消费的速度跟不上生产的速度时,就存在背压的问题,我们可以采用Flowable
替换Observable
来解决背压的问题.
subscribeOn()
给上面的代码分配线程Schedulers.io()
最终会通过线程池来进行管理,因此后面执行的任务都是在子线程中进行.Schedulers.io() == IoScheduler(持有线程池)
)
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)
)
HandlerScheduler(new Handler(Looper.getMainLooper()))
;RxJava中onserveOn(AndroidSchedulers.mainThread())流程图:
观察者Observer
的回调方法中会返回一个Disposable
对象,我们在页面销毁的时候,需要判断这个对象Disposable
是否销毁dispose
了,如果没有销毁需要将其销毁.
这样的目的是为了防止内存泄漏,解决在页面销毁的时候,还在执行后面onNext
和onComplete
的逻辑操作的问题.
代码如下:
//使用结果赋值给一个成员变量,在生命周期结束时销毁他
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();
}
}
主要是通过自定义Observable
继承自Observable
,重写subscribeActual(observer)
方法,然后在该方法中通过source.subscribe(包装类)
,将我们封装了下一层Observer
的包装类
传递进来封装包裹;包装类需要实现Disposable
,达到可以被中断的目的,同时需要包含下一层包裹Observer
变量,以便一层层调用每一层包裹Observer
的方法.
总结:
整体实现流程
- 通过由上往下一层一层调用Observable的各种方法,创建出Observable的具体实现类.
比如:通过调用create()方法,会返回ObservableCreate实现类,通过map()方法会返回ObservableMap实现类,通过observeOn()会返回OnservableObserveOn实现类;总之,就是在Observable后面拼接方法名称构成一个对象.- 通过由下往上调用Observable.subscribe(observer)方法封装包裹,接着调用具体实现类中的subscribeActual(observer)方法来完成,最终将封装的包裹通过这种方式传入:
source.subscribe(new 包装类(observer);
参数说明:
source:
表示create方法中通过new传入的ObservableOnSubscribe对象
observer:
表示下一层封装的包裹,每一个包裹都是一个包装类,包装类都实现了Observer接口
subscribe():
方法表示ObservableOnSubscribe重写的subscribe(包装类
)方法,里面的参数对应每一层的包装类;一般包装类调用onNext方法时,就会调用到包装类中的Observer对象onNext方法,依次达到一层一层往下拆包裹的目的.- 通过由上往下依次调用每个
具体实现类
.包装类
中的onNext方法时,就会直接调用调用observer.onNext方法,这里observer对象就是下一层包裹,因为每一层包裹都实现了Observer接口,以此达到了一层一层往下调用每一层包裹中onNext方法的目的.
文章浏览阅读63次。题目给定一个整数 n,返回 n! 结果尾数中零的数量。解题思路每个0都是由2 * 5得来的,相当于要求n!分解成质因子后2 * 5的数目,由于n中2的数目肯定是要大于5的数目,所以我们只需要求出n!中5的数目。C++代码class Solution {public: int trailingZeroes(int n) { ...
文章浏览阅读992次,点赞27次,收藏15次。UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。文件字节输入流:每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1。注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。UTF-8字符集:汉字占3个字节,英文、数字占1个字节。GBK字符集:汉字占2个字节,英文、数字占1个字节。GBK规定:汉字的第一个字节的第一位必须是1。_outputstream释放
文章浏览阅读1.8k次,点赞3次,收藏3次。解决jeecgboot每次登录进去都会弹出请重新登录问题,在utils文件下找到request.js文件注释这段代码即可_jeecg 登录自动退出
文章浏览阅读3.4k次。我国目前普遍采用需要系数法和二项式系数法确定用电设备的负荷,其中需要系数法是国际上普遍采用的确定计算负荷的方法,最为简便;而二项式系数法在确定设备台数较少且各台设备容量差..._数据中心用电负荷统计变压器
文章浏览阅读7k次,点赞4次,收藏46次。HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水平作业_网页设计成品百度网盘
文章浏览阅读392次。jailhouse 文章翻译,Look Mum, no VM Exits!_jailhouse sr-iov
文章浏览阅读751次。本文由chatgpt生成,文章没有在chatgpt生成的基础上进行任何的修改。以上只是chatgpt能力的冰山一角。作为通用的Aigc大模型,只是展现它原本的实力。对于颠覆工作方式的ChatGPT,应该选择拥抱而不是抗拒,未来属于“会用”AI的人。AI职场汇报智能办公文案写作效率提升教程 专注于AI+职场+办公方向。下图是课程的整体大纲下图是AI职场汇报智能办公文案写作效率提升教程中用到的ai工具。_python 删除文件特定几行
文章浏览阅读2.1k次。【代码】Java过滤特殊字符的正则表达式。_java正则表达式过滤特殊字符
文章浏览阅读5.7k次,点赞4次,收藏17次。css中背景的设置至关重要,也是一个难点,因为属性众多,对应的属性值也比较多,这里详细的列举了背景相关的7个属性及对应的属性值,并附上演示代码,后期要用的话,可以随时查看,那我们坐稳开车了······1: background-color 设置背景颜色2:background-image来设置背景图片- 语法:background-image:url(相对路径);-可以同时为一个元素指定背景颜色和背景图片,这样背景颜色将会作为背景图片的底色,一般情况下设置背景..._background设置背景图片
文章浏览阅读2.6k次,点赞2次,收藏8次。Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程
文章浏览阅读10w+次,点赞653次,收藏3k次。Windows安装pycharm教程新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入下载安装PyCharm1、进入官网PyCharm的下载地址:http://www.jetbrains.com/pycharm/downl_pycharm2021
文章浏览阅读835次。本节书摘来自异步社区出版社《跨境电商——速卖通搜索排名规则解析与SEO技术》一书中的第1章,第1.1节,作者: 冯晓宁,更多章节内容可以访问云栖社区“异步社区”公众号查看。1.1 初识速卖通的搜索引擎1.1.1 初识速卖通搜索作为速卖通卖家都应该知道,速卖通经常被视为“国际版的淘宝”。那么请想一下,普通消费者在淘宝网上购买商品的时候,他的行为应该..._跨境电商 速卖通搜索排名规则解析与seo技术 pdf