一.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>() {@Overridepublic 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观察者流程时,不按照上面的步骤来:
- 查看Observer源码
- 定义了Observer接口的方法,比如:onSubscribe、onNext、onError、onComplete
- 然后在使用的时候,直接创建自定义观察者,将new新建的Observer传入作为参数,重写实现方法.
- 了解Observable创建过程,分析源码
- 调用create()方法会创建ObservableCreate对象
- 将自定义(ObservableOnSubscribe)source资源传入ObservableCreate对象,作为一个参数
- 了解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类型@Overridepublic 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)tsource.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//由于我们做过拦截,所以这里传入的是包装后的ObserverCreateEmitter<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来解决背压的问题.
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;}
- Schedulers.io()->(Scheduler)Schedulers.IO->new IOTask()->IOTask.run()->IoHolder.DEFAULT->new
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,以确保代码执行在主线程中.
- AndroidSchedulers.mainThread()->(Scheduler)Schedulers.MAIN_THREAD->MainHolder.DEFAULT->new
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();//结束生命周期销毁disposableif (mDisposable != null && !mDisposable.isDisposed()){mDisposable.dispose();}
}
自定义RxView操作符
主要是通过自定义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方法的目的.
相关文章:
一.RxJava
1.RxJava使用场景 RxJava核心思想 Rx思维:响应式编程,从起点到终点,中途不能断掉,并且可以在中途添加拦截. 生活中的例子: 起点(分发事件,我饿了)->下楼->去餐厅->点餐->终点(吃饭,消费事件) 程序中的例子: 起点(分发事件,点击登录)->登录API->请求服务器-…...
如何使用 VSCode 软件运行C代码
VSCode 的下载和扩展的配置可以参考文章:VSCode 的安装与插件配置。 VSCode 是很好用的编辑器,通过给其配置 MinGW-w64 插件就可以在它上面编译运行C代码了。 在没有配置 MinGW-w64 插件时,在 VSCode 中运行下面的代码后打印如下图所示。 这…...
C# 调用Matlab打包的 DLL文件(傻瓜式操作)
1、准备Matlab代码 2. 打包 在matlab命令行窗口输入deploytool,打开MATLAB Complier,选择Library Compiler 在TYPE中选择.NET Assembly;在EXPORTED FUNCTIONS中选择要打包的文件;可以选择为自己打包的文件自定义NameSpace名称,本例中将NameSpace定义为…...
微信小程序学习实录3(环境部署、百度地图微信小程序、单击更换图标、弹窗信息、导航、支持腾讯百度高德地图调起)
百度地图微信小程序 一、环境部署1.need to be declared in the requiredPrivateInfos2.api.map.baidu.com 不在以下 request 合法域名3.width and heigth of marker id 9 are required 二、核心代码(一)逻辑层index.js(二)渲染层…...
【面试题】中高级前端工程师都需要熟悉的技能--前端缓存
前端缓存 一、前言二、web缓存分类1. HTTP缓存:2. 浏览器缓存:3. Service Worker:4. Web Storage缓存:5. 内存缓存: 三、http缓存详解1、http缓存类型a. 基于有效时间的缓存控制:b. 基于资源标识的缓存&…...
小红书数据分析:首播卖6亿,小红书直播开启新纪元!
5月22日,章小蕙在小红书开启了第一场带货直播。继董洁之后,小红书又迎来一位超级带货KOL。 据千瓜数据显示,相关话题#章小蕙小红书直播#上线不到30天,话题浏览量就高达2814.89万,笔记互动量达22.24万。 图 | 千瓜数据…...
Weex中,关于组件的水平排列竖直排列居中对齐居左对齐居右对齐低部对齐顶部对齐布局对齐说明
容器内子组件排列方向 子组件竖直方向排列(默认) 子组件水平方向排列 <style> .container {flex-direction: row;direction: ltr; } </style>子组件在父组件容器中的对齐方式 我们主要使用两个属性实现子组件在父组件的对齐方式ÿ…...
服务(第二十八篇)rsync
配置rsync源服务器: #建立/etc/rsyncd.conf 配置文件 vim /etc/rsyncd.conf #添加以下配置项 uid root gid root use chroot yes #禁锢在源目录 address 192.168.80.10 …...
Vue 3 第二十五章:插件(Plugins)
文章目录 1. 创建插件2. 使用插件3. 插件选项 Vue 3 的插件系统允许我们扩展 Vue 的功能和行为,并且可以在多个组件之间共享代码和逻辑。插件可以用于添加全局组件、指令、混入、过滤器等,并且可以在应用程序启动时自动安装。 1. 创建插件 创建插件需要…...
Android 系统内的守护进程 - main类服务(3) : installd
声明 只要是操作系统,不用说的就是其中肯定会运行着一些很多守护进程(daemon)来完成很多杂乱的工作。通过系统中的init.rc文件也可以看出来,其中每个service中就包含着系统后台服务进程。而这些服务被分为:core类服务(adbd/servicemanager/healthd/lmkd/logd/vold)和mai…...
华为OD机试真题 Java 实现【对称字符串】【2023Q2 200分】
一、题目描述 对称就是最大的美学,现有一道关于对称字符串的美学。 已知: 第 1 个字符串:R 第 2 个字符串:BR 第 3 个字符串:RBBR 第 4 个字符串:BRRBRBBR 第 5 个字符串:RBBRBRRBBRRBRBBR …...
day18文件上传下载与三层架构思想
servlet文件上传 注意事项:在写了响应后,若后面还需要执行代码,需要添加return; apach的servlet3.0提供了文件上传的功能. **在客户端中的jsp如何上传文件:**使用form标签 使用input标签type的file属性 form表单中的的enctype必须加:使用二进制的方式进行传输,否则不能进行…...
Async/await详解
一、概念与背景 他是在ES8被提出的一种异步方式,它其实是promise的一种语法糖 二、 Async关键字 async 关键字用于快速声明异步函数 ,可以用在函数声明、函数表达式、箭头函数和方法上 async function foo() {} let bar async function() {}; let…...
Mysql基础 — DDL、DML、DQL、DCL、函数、约束
文章目录 Mysql基础一、数据模型1.1 关系型数据库与非关系型数据库1.2 Mysql 数据模型 二、SQL2.1 SQL 通用语法2.2 SQL分类2.3 DDL2.3.1 数据库操作2.3.2 表操作 — 创建 & 查询2.3.3 表操作— 修改&删除2.3.4 数据类型2.3.4.1 数值类型2.3.4.2 字符串类型2.3.4.3 日期…...
中国移动董宁:深耕区块链的第八年,我仍期待挑战丨对话MVP
区块链技术对于多数人来说还是“新鲜”的代名词时,董宁已经成为这项技术的老朋友。 董宁2015年进入区块链领域,现任中国移动研究院技术总监、区块链首席专家。作为“老友”,董宁见证了区块链技术多个爆发式增长和平稳发展的阶段,…...
AI孙燕姿项目实现
最近在b站刷到很多关于ai孙笑川唱的歌曲,加上最近大火的ai孙燕姿, 这下“冷门歌手”整成热门歌手了 于是写下一篇文章, 如何实现属于的ai歌手。 注意滥用ai,侵犯他人的名誉是要承担法律责任的 下面是一些所需的文件链接ÿ…...
传统机器学习(六)集成算法(2)—Adaboost算法原理
传统机器学习(六)集成算法(2)—Adaboost算法原理 1 算法概述 Adaboost(Adaptive Boosting)是一种自适应增强算法,它集成多个弱决策器进行决策。 Adaboost解决二分类问题,且二分类的标签为{-1,1}。注:一定是{-1,1},不能是{0,1} …...
性能优化常用的技巧,你都知道吗?
在实际工作中,提升MySQL数据库的查询性能是非常重要的。除了基本的索引和查询优化技巧外,还有一些更深层次的优化方案可以进一步优化性能。 1. 数据库表设计优化 选择字段类型: 根据数据类型和范围,选择适当的字段类型。例如&am…...
机器学习——损失函数(lossfunction)
问:非监督式机器学习算法使用样本集中的标签构建损失函数。 答:错误。非监督式机器学习算法不使用样本集中的标签构建损失函数。这是因为非监督式学习算法的目的是在没有标签的情况下发现数据集中的特定结构和模式,因此它们依赖于不同于监督式…...
小航助学2022年NOC初赛图形化(小高组)(含题库答题软件账号)
需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统(含题库答题软件账号)_程序猿下山的博客-CSDN博客 单选题3.0分 删除编辑 答案:C 第1题如果要控制所有角色一起朝舞台区右侧移动,下面哪个积木块是不需要的? A…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
