RxJava 系列六:RxJava中的 Hook 技术
- 本文概述:
- 文章以Hook 技术为主题,分析RxJava 操作符源码执行流程,寻找出“钩子”着力点;实现了预期业务需求,同时总结并分析了使用Hook 为什么不要破坏原有流程并给出了反例代码与错误截图;
- 业务需求:期望检测项目中所有使用到RxJava 任意操作符的地方;
-
业务流程:在操作符对象返回前做拦截
- Hook为什么是全局的(因为这个是静态的)
源码分析:以 .create操作符为例
-
源码支撑:任何操作符都是这样,以.create操作符为例
-
onAssembly 干了什么?
-
代码展示:RxJava 2.x
- 业务流程:将输入的对象返回,但向用户开放了二次开发入口(onObservableAssembly)
-
补充代码: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
- 此时,我们只需要调用这个函数,给他传递个值就行了
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 { } }) ; } } -
运行截图:
怎么去破坏项目中的所有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; // 不破坏人家的功能 } }); -
错误原因:空指针异常