RxJava 系列六:RxJava中的 Hook 技术

429 阅读3分钟

RxJava 系列六:RxJava中的 Hook 技术

  • 本文概述:
    • 文章以Hook 技术为主题,分析RxJava 操作符源码执行流程,寻找出“钩子”着力点;实现了预期业务需求,同时总结并分析了使用Hook 为什么不要破坏原有流程并给出了反例代码与错误截图;
  • 业务需求:期望检测项目中所有使用到RxJava 任意操作符的地方;
  • 业务流程:在操作符对象返回前做拦截

    • Hook为什么是全局的(因为这个是静态的)

    image-20220627223430045

源码分析:以 .create操作符为例

  • 源码支撑:任何操作符都是这样,以.create操作符为例

    image-20220627221719532

  • onAssembly 干了什么?

    • 代码展示:RxJava 2.x

      • 业务流程:将输入的对象返回,但向用户开放了二次开发入口(onObservableAssembly)

      image-20220627221852857

    • 补充代码:RxJava 1.x

      • 业务流程:将输入的对象返回,在设计时就预留了优化空间
       @NonNull
       public static <T> Observable<T> onAssembly(@NonNull Observable<T> source) {
           // 提前预留 给 2.x
           return source;
       }
      
  • Hook思路:

    • 默认情况下,f 为null;将传入的对象直接返回;那么,我们想要这段代码执行,那么就需要让这个f 不为null ,自己去new 一个Function;将这个Function放进去,这样当RxJava在执行任何操作符的时候,都会优先执行我们的逻辑了
  • 怎么使得优先执行我们的操作:分析源码(寻找 f --->onObservableAssembly的赋值来源)

    • 只有在这个地方才有:onObservableAssembly

      image-20220627222838227

    • 此时,我们只需要调用这个函数,给他传递个值就行了
     RxJavaPlugins.setOnObservableAssembly(new Function<Observable, Observable>() {
                 @Override
                 public Observable apply(Observable observable) throws Exception {
                     Log.d(Flag.TAG, "apply: 整个项目 全局 监听 到底有多少地方使用 RxJava:" + observable);
     ​
                     // 伪代码
                     /*if (observable  === ObservableMap)
                         return null;*/
     ​
                     return null; // 不破坏人家的功能
                 }
             });
    
    • 编写细节:这个函数是怎么找到的

      • 使用快捷键CTRL+F,查找文本setOnObservableAssembly

        • 源码中就只有图片示例一处调用,那么我们调用这个函数,即可向源码原执行流程中插入自定义逻辑
    • 编写细节:可不可以将 new Function<泛型1,泛型2>泛型2改为null?

      • 不能改,Hook技术不能破坏原有的稳定性;
      • 因为你改了以后,凡是RxJava 操作符都会走这个Function,结合Rx 响应式编程思想(以事件流动推进业务);如果泛型2为null,意味着下一个事件/订阅处(Rx 流程终点处))输入端为空,进而引发空指针异常
      • 这个并不是一无是处,可以用来破坏项目中的所有Rx 操作符
  • 代码展示:SourceActivity1

     package com.xiangxue.rxjavademo.source;
     ​
     import android.os.Bundle;
     import android.util.Log;
     ​
     import androidx.appcompat.app.AppCompatActivity;
     ​
     import io.reactivex.Observable;
     import io.reactivex.ObservableEmitter;
     import io.reactivex.ObservableOnSubscribe;
     import io.reactivex.functions.Consumer;
     import io.reactivex.functions.Function;
     import io.reactivex.plugins.RxJavaPlugins;
     ​
     /**
      * TODO RxJava Hook 点
      */
     public class SourceActivity1 extends AppCompatActivity {
     ​
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
     ​
             Log.d("sourceShow", "apply: 整个项目 全局 监听 到底有多少地方使用");
     ​
             //
             // 我想用了多少给 Map操作   flatMap
             // Hook之前的监听
             RxJavaPlugins.setOnObservableAssembly(new Function<Observable, Observable>() {
                 @Override
                 public Observable apply(Observable observable) throws Exception {
                     Log.d("sourceShow", "这个地方使用了RxJava 操作符" + observable);
     ​
                     // 伪代码
                     /*if (observable  === ObservableMap)
                         return null;*/
     ​
                     return observable; // 不破坏人家的功能
                 }
             });
     ​
             testHook();
         }
     ​
         /**
          * TODO RxJava Hook 点
          */
         public static void testHook() {
             Observable.create(new ObservableOnSubscribe<Object>() {
                 @Override
                 public void subscribe(ObservableEmitter<Object> e) throws Exception {
                     e.onNext("A");
                 }
             })
     ​
                     // null.map
             .map(new Function<Object, Boolean>() {
                 @Override
                 public Boolean apply(Object o) throws Exception {
                     return true;
                 }
             })
             //订阅一下
             .subscribe(new Consumer<Boolean>() {
                 @Override
                 public void accept(Boolean aBoolean) throws Exception {
     ​
                 }
             })   ;
         }
     }
    
  • 运行截图:

    图片.png

怎么去破坏项目中的所有RxJava 操作符

  • 将setOnObservableAssembly 修改返回值为null

    • 具体原因,以在上文浅析,再次不再赘述;
    • 关于RxJava基础知识,请各位看官查阅本专栏RxJava系列往期文章:juejin.cn/column/7112…
     RxJavaPlugins.setOnObservableAssembly(new Function<Observable, Observable>() {
         @Override
         public Observable apply(Observable observable) throws Exception {
             Log.d("sourceShow", "这个地方使用了RxJava 操作符" + observable);
     ​
             // 伪代码
             //                if (observable  == ObservableMap)
             //                    return null;
     ​
             return null; // 不破坏人家的功能
         }
     });
    
  • 错误原因:空指针异常

    image-20220627224735245