RxJava 系列(十):自定义防抖操作符

518 阅读3分钟

RxJava 系列(十):自定义防抖操作符

本文概述:

  • 此为RxJava 系列第十篇文章,文章以自定义RxJava 操作符为主题,实现了为按钮控件添加的防抖操作符;文章详细展示了代码编写过程,具有一定借鉴意义;

业务需求

  • 为按钮控件编写一个防抖操作符

具体编辑过程

工程结构展示:

图片.png

RxView:

  • 暴露给用户使用:静态

     public static Observable<Object> clicks(View view) {
         return new ViewClickObservable(view);
     }
    
    • 细节一:为什么要返回 Observable (观察系统操作符的实现)

      image-20220704210640653

    • 细节二:为什么不写成泛型T

      • 为了简化,不用让用户去指定泛型
    • 细节三:返回的什么东西(自定义的Observable 的子类)

      image-20220704215108555

  • 完整代码:

    图片.png

ViewClickObservable

搭架子:ViewClickObservable

  • 类签名:指定继承关系

     public class ViewClickObservable extends Observable<Object> {
    
    • 细节一:Observable 子类在源码中的类签名是怎么干的?

      • 传递了一个泛型给Observable,但在前面已经指定了泛型为Object
  • 重写方法:subscribeActual

     @Override
     protected void subscribeActual(Observer<? super Object> observer) {
    
    • 为什么要重写这个:根据源码执行流程,会调用到这个方法的
  • 定义View:因为我们是对View操作(为按钮控件添加防抖操作符)

     private final View view;
    
  • 定义控件的关联事件:最终是没有用到的

     private static Object EVENT2;
    
  • 将View 作为参数传给ViewClickObservable 构造方法

     public ViewClickObservable(View view) {
         this.view = view;
     ​
         EVENT2 = view;
     }
    
  • 定义本类中的包裹:是可以中断的

    • 重写三个方法

      • 监听、中断、检测中断
  • 架子就搭好了

    image-20220704212133666

添加业务代码:ViewClickObservable

  • 丰富包裹

    • 传入View ,下一层的包裹,添加构造函数
    • 定义原子变量:作为中断的标记信号

    image-20220704212439224

    • 丰富监听函数:通过检测中断标志位,没有被中断则向下一层分发事件

    image-20220704212551471

    • 丰富当中断时的操作:直接返回中断信号

    图片.png

    • 丰富中断操作:提供给用户的,让事件流动中断掉

      • 没有中断时,才能去取消事件流动

      image-20220704213022998

      • 主线程中的中断操作:直接将监听器置空即可

      image-20220704213125372

      • 子线程:使用Handler 切换到主线程后去中断(将监听器置空)

        • 简单操作

        image-20220704213335878

        • 借鉴源码方式进行线程切换:查看文末 重点探究一

  • 丰富重写函数:subscribeActual

    • 实例化包裹

    image-20220704214735550

    • 当订阅后将包裹向下传递

    image-20220704214945154

    • 关联控件

    image-20220704214958671

完整代码:ViewClickObservable

 public class ViewClickObservable extends Observable<Object> {
 ​
     private final View view;
 ​
     // 事件  第一节课 防抖 事件 没用
     private static final Object EVENT = new Object();
     private static Object EVENT2;
 ​
     public ViewClickObservable(View view) {
         this.view = view;
 ​
         EVENT2 = view;
     }
 ​
     @Override
     protected void subscribeActual(Observer<? super Object> observer) {
         // 实例化包裹
         MyListener myListener = new MyListener(view, observer);
         //当订阅后将包裹向下传递
         observer.onSubscribe(myListener);
         //关联控件
         this.view.setOnClickListener(myListener);
     }
 ​
     // 我们的包裹
     static final class MyListener implements View.OnClickListener, Disposable {
 ​
         private final View view;
         private Observer<Object> observer;  // 存一份 下一层
 ​
         // 原子性,同学们自行看看文章
         // https://www.jianshu.com/p/8a44d4a819bc
         // boolean  == AtomicBoolean
         private final AtomicBoolean isDisposable = new AtomicBoolean();
 ​
         public MyListener(View view, Observer<Object> observer) {
             this.view = view;
             this.observer = observer;
         }
 ​
         @Override
         public void onClick(View v) {
             if (isDisposed() == false) {
                 observer.onNext(EVENT);
             }
 ​
         }
 ​
         // 如果用调用了 中断
         @Override
         public void dispose() {
             // 如果没有中断过,才有资格,   取消view.setOnClickListener(null);
             if (isDisposable.compareAndSet(false, true)) {
                 // 主线程 很好的中断
                 if (Looper.myLooper() == Looper.getMainLooper()) {
                     view.setOnClickListener(null);
 ​
                 } else { // 此时为子线程,那么通过Handler到主线程后,才执行中断(将监听器置空)
                     new Handler(Looper.getMainLooper()) {
                         @Override
                         public void handleMessage(@NonNull Message msg) {
                             super.handleMessage(msg);
                             view.setOnClickListener(null);
                         }
                     };
 ​
                     //                     HandlerScheduler.scheduleDirect
 ​
                     AndroidSchedulers.mainThread().scheduleDirect(new Runnable() {
                         @Override
                         public void run() {
                             view.setOnClickListener(null);
                         }
                     });
                 }
             }
         }
 ​
         @Override
         public boolean isDisposed() {
             return isDisposable.get();
         }
     }
 }

重点探究一:源码中是怎么切换到主线程的?

  • 借助了HandlerScheduler

    image-20220704213533805

  • 查阅HandlerScheduler.scheduleDirect

    • 接收了Runnable,并且是运行在主线程中的;结合此时的业务需求(需要从子线程切换到主线程后,执行事件中断操作(将监听器置空))

    image-20220704214023595

  • 尝试一:直接使用HandlerScheduler.scheduleDirect ,将Runnable 丢进去就行了

    • 报错了

    image-20220704214123713

    • 因为HandlerScheduler 是一个final 类型的

    image-20220704214246309

  • 尝试二:通过AndroidSchedulers.mainThread()

    • 最终是会调用到HandlerScheduler.scheduleDirect (系列前部分文章已经分析过了)

    • 将Runnable 传给它,就实现了线程的切换了

    图片.png

业务层代码:RxActivity

image-20220704215404830

运行结果:

  • 疯狂点击:2 秒中才响应一次

图片.png