RxJava 系列(三):RxJava操作符(RxBinding与flatMap)的应用场景及其使用细节
本文概述:
- 什么是”抖动”,如何避免抖动;在使用RxJava处理多层嵌套数据的时如何解决代码冗余问题Java;
- 本文为RxJava系列文章第三篇,介绍了RxBinding操作符以及flatMap 操作符基本使用,操作细节,底android是什么系统层原理
- 往期精彩请查阅个人开源框架专栏:/column/7112…
抖动现象:
- 在一秒中android的drawable类点击按钮20次,造嵌套函数成服务器响应20次,这个就是抖动
- 抖初始化动会徒增服务器压力,消耗不必要的资源
采用RxBinding防抖:
-
添加相关依赖
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' // 操作功能防抖
-
定位需要防止抖动的控件:能点击都是可以用的按钮开关怎么接
// 对那个控件防抖动? Button bt_anti_shake = findViewById(R.id.bt_anti_shake);
-
添加RXBinding代码
//使用RxBinding RxView.clicks(bt_anti_shake)
-
细节:RxBinding可以防止任何控件android/harmonyos的抖按钮的文字符号动(入参为View)
-
细节:new Consumer存储事件的泛型是写死了
但是map的话就是没有写死按钮图片的,返回传入的事件类型
-
细节:代码报黄该怎么办(添加一个注解就行了)
//这样两层嵌套还是可以,再多了就太麻烦了 @SuppressLint("CheckResult") private void antiShakeActon() { …… }
-
-
防抖完整代码:
//这样两层嵌套还是可以,再多了就太麻烦了 @SuppressLint("CheckResult") private void antiShakeActon() { // 注意:(项目分类)查询的id,通过此id再去查询(项目列表数据) // 对那个控件防抖动? Button bt_anti_shake = findViewById(R.id.bt_anti_shake); //使用RxBinding RxView.clicks(bt_anti_shake) // 2秒钟之内 响应你一次 .throttleFirst(2000, TimeUnit.MILLISECONDS) //为什么这里接收的是Object,因为源码已经写死了 .subscribe(new Consumer<Object>() { @Override //这个 o 指代的就是需要防抖的控件 public void accept(Object o) throws Exception { // 查询总数据 api.getProject() //调用封装好的线程库,给上面分配异步线程,给下面分配主线程 .compose(DownloadActivity.rxud()) //ProjectBean:此时的事件是主数据的JavaBean .subscribe(new Consumer<ProjectBean>() { @Override public void accept(ProjectBean projectBean) throws Exception { //一个id就是一个Data(指代的是总数据的id) for (ProjectBean.DataBean dataBean : projectBean.getData()) { // 10 // 查询item数据:getId指代item数据的id(根据主数据的id,拿到item的id) api.getProjectItem(1, dataBean.getId()) //调用封装好的线程库,给上面分配异步线程,给下面分配主线程 .compose(DownloadActivity.rxud()) .subscribe(new Consumer<ProjectItem>() { @Override public void accept(ProjectItem projectItem) throws Exception { // 此时是可以UI操作 Log.d(TAG, "accept: " + projectItem); } }); } } }); } }); }
-
对防抖代码进行初始化:不初始化是无法定位哪一个控件需要防抖
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_use); //API初始化:Retrofit初始化 api = HttpUtil.getOnlineCookieRetrofit().create(WangAndroidApi.class); // 功能防抖 + 网络嵌套 // antiShakeActon(); antiShakeActonUpdate(); }
- 此时Button的任何事件都交给了防抖函数进行处理
-
运行结果:防抖操作的必要性
-
但是现在有一个很严重的按钮英文翻译问题:嵌套太深导致代码结构不美观按钮英文翻译,阅读体验极差
-
引入新的Rx 操作符:flatMap
-
解决嵌套问题:fl按钮atMap
整体描述:
-
一句话描述:你给它一个数据,它还给你多个数据
-
示意图:
-
举例说明:flatMap不仅可以传androidstudio安装教程递数据,其还具有额外的分发能力
编写具体代码:
-
业务需求:通过RxBinding解决抖动问题,通过flatMap卡片解决前者带来的嵌套问题
-
指定需要按钮防抖的控件:
// 对那个控件防抖动? Button bt_anti_shake = findViewById(R.id.bt_anti_shake);
-
使用RxBinding操作符处理抖动:设置2秒钟之内只响应一次
// 2秒钟之内 响应你一次 RxView.clicks(bt_anti_shake) .throttleFirst(2000, TimeUnit.MILLISECONDS)
-
为下面分配异步线程
// 我只给下面 切换 异步 .observeOn(Schedulers.io())
-
添加flatMap卡片
//使用flatMap卡片 .flatMap(new Function<Object, ObservableSource<?>>() { })
-
细节:为什么new Functioandroid平板电脑价格n<param1,param2>的第一个泛型参数是Object
-
因为此时事件的起点在RxView.clicks(bt_anjava编译器ti_shak初始化是什么意思e)这个地方,而RxView.clicks里面的事件类型就是Object是写死了的
-
-
细节:为什么第二个泛型参数的类型默认是 ObservableSource<?>
- 通过这个包装类,就可以实现flatMap虽然只接受一条数据,但是其可以向后分发多条数据
- 可嵌套查询sql语句以对比map卡片,这个嵌套循环东西的第二泛型参数就是一个基本类型而已
- 这个里面的问号实际上是一个通配符,但是根据业务需要并不需要其通配,仅指代总数据的 JavaBean即可
- 这个通配符是可以避免的,使用L嵌套函数amda表达式就行了,但是,使用Lamda表达式后会导致代码的可读性下降
-
flatMap 改造后的代码
.flatMap(new Function<Object, ObservableSource<ProjectBean>>() { @Override //返回的是主数据的 JavaBean public ObservableSource<ProjectBean> apply(Object o) throws Exception { return api.getProject(); // 主数据 } })
-
还有个问题:如何代码写成这个样子会不会报错?
@SuppressLint("CheckResult") private void antiShakeActonUpdate() { // 注意:项目分类查询的id,通过此id再去查询(项目列表数据) // 对那个控件防抖动? Button bt_anti_shake = findViewById(R.id.bt_anti_shake); RxView.clicks(bt_anti_shake) .throttleFirst(2000, TimeUnit.MILLISECONDS) // 2秒钟之内 响应你一次 //使用flatMap卡片 .flatMap(new Function<Object, ObservableSource<ProjectBean>>() { @Override //返回的是主数据的 JavaBean public ObservableSource<ProjectBean> apply(Object o) throws Exception { return api.getProject(); // 主数据 } }) .subscribe(new Consumer<ProjectItem>() { @Override public void accept(ProjectItem projectItem) throws Exception { // 如果我要更新UI 会报错2 不会报错1 Log.d(TAG, "accept: " + projectItem); } }); }
-
肯定是会报错的:这样写—>使用主线程请求服务器了
- 防抖是运行在主线程的,而这个flatMap 卡片应运行在异步线程;但是,在这个代码里面并无线程的切换
-
-
有个比较吊的切换线程的方式:只给下面切换异步线程
@SuppressLint("CheckResult") private void antiShakeActonUpdate() { // 注意:项目分类查询的id,通过此id再去查询(项目列表数据) // 对那个控件防抖动? Button bt_anti_shake = findViewById(R.id.bt_anti_shake); RxView.clicks(bt_anti_shake) .throttleFirst(2000, TimeUnit.MILLISECONDS) // 2秒钟之内 响应你一次 // 我只给下面 切换 异步 .observeOn(Schedulers.io()) //使用flatMap卡片 .flatMap(new Function<Object, ObservableSource<ProjectBean>>() { @Override //返回的是主数据的 JavaBean public ObservableSource<ProjectBean> apply(Object o) throws Exception { return api.getProject(); // 主数据 } }) .subscribe(new Consumer<ProjectItem>() { @Override public void accept(ProjectItem projectItem) throws Exception { // 如果我要更新UI 会报错2 不会报错1 Log.d(TAG, "accept: " + projectItem); } }); }
- 为什么.observeOn(Schedulers.io())可以切换线程(这个是源码里面的东西)
-
-
实现flatMap 向后分发多个数据的功能:此时分发的是总数据
//现在要开始向后分发多个数据 .flatMap(new Function<ProjectBean, ObservableSource<ProjectBean.DataBean>>() { @Override public ObservableSource<ProjectBean.DataBean> apply(ProjectBean projectBean) throws Exception { // 我自己搞一个发射器 发多次 10次(内部接收一个类似迭代器的东西--->这个参数跟上面的for循环一个效果,projectBean内部有10个数据,那么就分发10次) return Observable.fromIterable(projectBean.getData()); } })s
-
实现分发item数据
//开始查询item数据 .flatMap(new Function<ProjectBean.DataBean, ObservableSource<ProjectItem>>() { @Override public ObservableSource<ProjectItem> apply(ProjectBean.DataBean dataBean) throws Exception { //查询第一页数据 return api.getProjectItem(1, dataBean.getId()); } })
-
此时抵达事件分发终点:
-
首先切换成主线程:flatMap 在异步线程
// 给下面切换 主线程 .observeOn(AndroidSchedulers.mainThread())
-
展示数嵌套查询sql语句据按钮开关符号,此时,是可以更新 UI 的
.subscribe(new Consumer<ProjectItem>() { @Override public void accept(ProjectItem projectItem) throws Exception { // 如果我要更新UI 会报错2 不会报错1 Log.d(TAG, "accept: " + projectItem); } });
-
其实嵌套,是初始化可以不使用flatMap的,替换成map也是按钮图片可以的(注意事件的包装,此时的事件类嵌套结构型应该是包装类:ObservableSource<具体的JavaBean>)
-