RxJava 系列八:Map 变换原理
-
本文概述:
- 文章以RxJava 重要拦截器map 为主题,以源码分析为目的;系统展示了map的源码执行流程,探讨了 map 的工作原理;并在文章末尾总结了拆包、解包流程;
Map干了什么
- 做类型变换:例如向map输入String类型事件,map输出 BitMap类型事件
代码分析:
-
代码展示
// 返回ObservableCreate对象,并且将自定义source传进去 Observable.create( //下面这一块就是自定义source new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> e) throws Exception { } }) // 由ObseravbleCreate.map执行 .map(new Function<String, Bitmap>() { @Override public Bitmap apply(String s) throws Exception { return null; } }) // 由ObservableMap.subscribe 执行 .subscribe( // 自定义观察者(事件流向的终点) new Observer<Bitmap>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Bitmap bitmap) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
流程是怎么玩的?
-
示意图:U型结构
-
第一次跳转:进入Observable.subscribe
- 将终点作为参数,传入订阅流程;Observable.subscribe 中调用 subscribeActual(observer);
-
第二次跳转:进入ObservableMap.subscribeActual();
-
这个是抽象函数,那么执行到其实现类中重写,执行到 ObservableMap.subscribeActual();
-
封装第一级包裹,将包裹拿给map的上一层(ObservableCreate)
@Override public void subscribeActual(Observer<? super U> t) { source.subscribe(new MapObserver<T, U>(t, function)); }
-
细节:这里的 source 不是业务代码中的自定义 source 而是事件流动中的 Map 卡片的上一事件
public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) { super(source); this.function = function; }
-
-
第三次跳转:进入ObservableCreate.subscribeActual();
-
以第一级包裹为参数封装第二级包裹
@Override protected void subscribeActual(Observer<? super T> observer) { CreateEmitter<T> parent = new CreateEmitter<T>(observer);
-
此时会触发终点处的重写订阅
-
通过业务代码中的自定义source.subscribe(parent),
//参数是第二级包裹 observer.onSubscribe(parent);
-
此时的 source 才是业务代码中的 自定义 source
public ObservableCreate(ObservableOnSubscribe<T> source) { this.source = source; }
-
-
第四次跳转:进入source.onNext()
-
第五次跳转:进入第二级包裹的 onNext
@Override public void onNext(T t) { if (t == null) { onError(new NullPointerException("XXX")); return; } if (!isDisposed()) { //进入第一级包裹的 onNext() observer.onNext(t); } }
-
第六次跳转:进入第一级包裹的 onNext
-
在这个里面做变换(T 是输入事件,R 是输出类型)
-
重点在这个地方:mapper.apply(t)
- 这里涉及一个接口
//T: map的输入事件,map的输出事件 public interface Function<T, R> { R apply(@NonNull T t) throws Exception; }
- 真正的实现:在业务代码中的map卡片里面
//业务代码中的map卡片(由用户指定了事件的输入输出) .map(new Function<String, Bitmap>() { @Override public Bitmap apply(String s) throws Exception { return null; } })
第一级包裹的 onNext
@Override public void onNext(T t) { if (done) { return; } if (sourceMode != NONE) { actual.onNext(null); return; } U v; try { v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return; } actual.onNext(v); }
-
-
第六次跳转细节:mapper实际上就是终点,map<T,R> 中的R 就是终点.onNext(t) 里面的 t
- 源码展示:为什么是这样
//返回的是R,意味着经过 map 卡片后输出的事件类型就是 R,那么终点.onNext(t)就是R @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) { ObjectHelper.requireNonNull(mapper, "mapper is null"); return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper)); }
-
重点代码展示: ObservableMap.subscribeActual
-
将终点传入,此时做了一层包裹,向下走开始拆包裹(典型的洋葱模型)
-
这个source是map的上一层:ObservableCreate
-
源码展示
-
-
重点代码展示: ObservableCreate.subscribeActual()
- 将第一级包裹作为参数,封装了第二级包裹parent(实际上就是发射器)
封包与拆包:
-
封包裹是为了拆包裹
-
封包与拆包:
- 向上走(黑色):封包
- 向下走(红色):拆包
-
好看的图片:
-
写了多少个 map 就会封多少次,拆多少次
-
为什么要这样写?
- 确保 Rx 编程的实现:可以保证思维不断的情况下,从起点到终点添加无数张卡片
补充:装饰模型
-
不断给它穿衣服:
- 流程是从上往下走
- 这种才是最正确的RxJava 结构