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)

      RxJava操作符(RxBinding与flatMap)的应用场景及其使用细节

    • 细节:new Consumer存储事件的泛型是写死了

      RxJava操作符(RxBinding与flatMap)的应用场景及其使用细节

      但是map的话就是没有写死按钮图片的,返回传入的事件类型

      RxJava操作符(RxBinding与flatMap)的应用场景及其使用细节

    • 细节:代码报黄该怎么办(添加一个注解就行了)

      //这样两层嵌套还是可以,再多了就太麻烦了
      @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的任何事件都交给了防抖函数进行处理
  • 运行结果:防抖操作的必要性

    RxJava操作符(RxBinding与flatMap)的应用场景及其使用细节

  • 但是现在有一个很严重的按钮英文翻译问题:嵌套太深导致代码结构不美观按钮英文翻译,阅读体验极差

    • 引入新的Rx 操作符:flatMap

解决嵌套问题:fl按钮atMap

整体描述:

  • 一句话描述:你给它一个数据,它还给你多个数据

  • 示意图:

    RxJava操作符(RxBinding与flatMap)的应用场景及其使用细节

  • 举例说明:flatMap不仅可以传androidstudio安装教程递数据,其还具有额外的分发能力

    RxJava操作符(RxBinding与flatMap)的应用场景及其使用细节

编写具体代码:

  • 业务需求:通过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是写死了的

      RxJava操作符(RxBinding与flatMap)的应用场景及其使用细节

    • 细节:为什么第二个泛型参数的类型默认是 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>)