学而不思则罔,思而不学则殆
我相信很多人初学RxJava的时候一定被里面的各种接口和回调绕晕了,各种回调,各种订阅,我在哪儿,我又在哪儿,因为博主就是,最开始学习的时候被绕晕了。所以本篇文章就带领读者从一种熟悉的逻辑结构中来学习和了解RxJava—双向链表。
个人理解:RxJava 是 一种链式调用链,原理是双向链表调用链+代理模式
首先什么是链式调用,常见的是就Builder(构建者模式)的用法,比如OkHttp中的Client构造:
OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
还比如Kotlin中:
fun main() {
//统计文件中字符出现的个数
File("MyKotlinWorld.iml")
.readText()
.toCharArray()
.filterNot(Char::isWhitespace)
.groupBy { it }
.map { it.key to it.value.count() }
.forEach(::println)
}
在RxJava中能够链式调用的关键元素时接口ObservableSource,所有的数据源或者数据处理都实现或者间接实现了该接口。
我相信很多小伙伴第一次学习RxJava的时候都会被该框架中的名词接口给绕晕。RxJava中使用的接口非常多,很多都会把人给绕晕,博主第一次看的时候很多接口都不是很理解。先来看一下RxJava中的接口
别看这么多接口,我们不都分析,作为入门篇介绍,我们只来介绍分析一下这三个接口。
接口 | 说明 |
---|---|
ObservableSource | 被观察者和订阅关系的实现 |
Observer | 观察者 |
Disposable | 中断观察的桥梁 |
@NonNull Observable<Integer> observable = Observable.just(1, 2, 3, 4);
System.out.println("observable:" + observable);
//2.建立观察者
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
System.out.println("onSubscribe:" + d);
// FIXME: 2020/11/11 如果想取消可以调用d.dispose(); 打断
}
@Override
public void onNext(@NonNull Integer integer) {
System.out.println("onNext:" + integer);
}
@Override
public void onError(@NonNull Throwable e) {
System.out.println("onError:" + e);
}
@Override
public void onComplete() {
System.out.println("onComplete");
}
};
//3.建立订阅关系
observable.subscribe(observer);
一般都是链式调用,用法如下:
private static void test2() {
Observable.just(1, 2, 3, 4).subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
System.out.println("onSubscribe:" + d);
// FIXME: 2020/11/11 如果想取消可以调用d.dispose(); 打断
}
@Override
public void onNext(@NonNull Integer integer) {
System.out.println("onNext:" + integer);
}
@Override
public void onError(@NonNull Throwable e) {
System.out.println("onError:" + e);
}
@Override
public void onComplete() {
System.out.println("onComplete");
}
});
}
结果如下:
observable:io.reactivex.rxjava3.internal.operators.observable.ObservableFromArray@42a57993
onSubscribe:io.reactivex.rxjava3.internal.operators.observable.ObservableFromArray$FromArrayDisposable@75b84c92
onNext:1
onNext:2
onNext:3
onNext:4
onComplete
我们来看一下建立订阅关系的subscribe方法。
//Observable.java
@Override
public final void subscribe(@NonNull Observer<? super T> observer) {
//判空
Objects.requireNonNull(observer, "observer is null");
try {
//转换处理
observer = RxJavaPlugins.onSubscribe(this, observer);
//判空
Objects.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null Observer. Please change the handler provided to RxJavaPlugins.setOnObservableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins");
//实际订阅处理逻辑
subscribeActual(observer);
} catch (NullPointerException e) { // NOPMD
throw e;
} catch (Throwable e) {
...
}
}
protected abstract void subscribeActual(@NonNull Observer<? super T> observer);
订阅的主要逻辑是在subscribeActual方法中实现的,该方法是一个抽象方法。具体的实现是在不同的被观察者实现的。
这个方法很重要,是重点。
我们就刚刚测试的just操作符来看一下subscribeActual的实现。
//Observable.java
public static <T> Observable<T> just(@NonNull T item1, @NonNull T item2, @NonNull T item3, @NonNull T item4) {
Objects.requireNonNull(item1, "item1 is null");
Objects.requireNonNull(item2, "item2 is null");
Objects.requireNonNull(item3, "item3 is null");
Objects.requireNonNull(item4, "item4 is null");
return fromArray(item1, item2, item3, item4);
}
public static <T> Observable<T> fromArray(@NonNull T... items) {
Objects.requireNonNull(items, "items is null");
if (items.length == 0) {
return empty();
}
if (items.length == 1) {
return just(items[0]);
}
return RxJavaPlugins.onAssembly(new ObservableFromArray<>(items));
}
just操作最终会生成ObservableFromArray的对象,该类传入一个泛型数组。
看一下ObservableFromArray的源码,ObservableFromArray实现了subscribeActual方法。
public final class ObservableFromArray<T> extends Observable<T> {
final T[] array;
public ObservableFromArray(T[] array) {
this.array = array;
}
@Override
public void subscribeActual(Observer<? super T> observer) {
FromArrayDisposable<T> d = new FromArrayDisposable<>(observer, array);
observer.onSubscribe(d);
if (d.fusionMode) {
return;
}
d.run();
}
...
}
当我们通过subscribe注册的时候,走到了subscribeActual方法。其中observer就是我们建立订阅关系时候实现的观察者对象。该对象被用来实例化一个新的FromArrayDisposable对象d,然后调用了FromArrayDisposable的run方法。看一下FromArrayDisposable类源码,忽略其中一些方法,只看主逻辑:
static final class FromArrayDisposable<T> extends BasicQueueDisposable<T> {
final Observer<? super T> downstream;
final T[] array;
...
FromArrayDisposable(Observer<? super T> actual, T[] array) {
this.downstream = actual;
this.array = array;
}
...
void run() {
T[] a = array;
int n = a.length;
for (int i = 0; i < n && !isDisposed(); i++) {
T value = a[i];
if (value == null) {
downstream.onError(new NullPointerException("The element at index " + i + " is null"));
return;
}
downstream.onNext(value);
}
if (!isDisposed()) {
downstream.onComplete();
}
}
}
该run方法很简单,for循环把之前传入的数组一个一个分发出去。时序图如下:
RxJava中的Observable有很多静态方法和非静态方法。
静态方法一般可以作为数据源,做为头部节点
非静态方法一般可以做为中间数据处理,做为中间节点
我们看几个非静态方法。
public final <U extends Collection<? super T>> Observable<U> buffer(int count, int skip, @NonNull Supplier<U> bufferSupplier) {
ObjectHelper.verifyPositive(count, "count");
ObjectHelper.verifyPositive(skip, "skip");
Objects.requireNonNull(bufferSupplier, "bufferSupplier is null");
return RxJavaPlugins.onAssembly(new ObservableBuffer<>(this, count, skip, bufferSupplier));
}
public final Observable<T> sample(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) {
Objects.requireNonNull(unit, "unit is null");
Objects.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSampleTimed<>(this, period, unit, scheduler, false));
}
public final Observable<T> scan(@NonNull BiFunction<T, T, T> accumulator) {
Objects.requireNonNull(accumulator, "accumulator is null");
return RxJavaPlugins.onAssembly(new ObservableScan<>(this, accumulator));
}
这几个Observable具体是干什么的我们先不管,就是他们都有一个共同点,就是都传入了一个this对象。这种情况感觉就像是套娃,我自己是一个Observable,但是呢,我自己还持有了一个Observable。
测试一个简单的链式调用,先通过just操作符发送4…9,在通过操作符startWithArray在4…9前面发送1,2,3。然后在通过map操作符进行对象转换成String类型。
private static void test() {
@NonNull Observable<Integer> observable0 = Observable.just(4, 5, 6, 7, 8, 9);
@NonNull Observable<Integer> observable1 = observable0.startWithArray(1, 2, 3);
@NonNull Observable<String> observable2 = observable1.map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) throws Throwable {
StringBuilder stringBuilder = new StringBuilder("" + integer).append(":");
for (Integer i = 0; i < integer; i++) {
stringBuilder.append("a");
}
return stringBuilder.toString();
}
});
System.out.println("observable0:" + observable0);
System.out.println("observable1:" + observable1);
System.out.println("observable2:" + observable2);
observable2
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
System.out.println("onSubscribe:" + d);
// FIXME: 2020/11/11 如果想取消可以调用d.dispose(); 打断
}
@Override
public void onNext(@NonNull String integer) {
System.out.println("onNext:" + integer);
}
@Override
public void onError(@NonNull Throwable e) {
System.out.println("onError:" + e);
}
@Override
public void onComplete() {
System.out.println("onComplete");
}
});
}
测试结果如下:
observable0:io.reactivex.rxjava3.internal.operators.observable.ObservableFromArray@6bc7c054
observable1:io.reactivex.rxjava3.internal.operators.observable.ObservableConcatMap@232204a1
observable2:io.reactivex.rxjava3.internal.operators.observable.ObservableMap@4aa298b7
onSubscribe:io.reactivex.rxjava3.internal.operators.observable.ObservableMap$MapObserver@28d93b30
onNext:1:a
onNext:2:aa
onNext:3:aaa
onNext:4:aaaa
onNext:5:aaaaa
onNext:6:aaaaaa
onNext:7:aaaaaaa
onNext:8:aaaaaaaa
onNext:9:aaaaaaaaa
onComplete
这整个流程在我的理解下,可以比喻成一个双向链表。其中每个操作符对应的Observable对象可以理解为双向链表中的每个节点Node.通过log信息我们知道共有三个Observable。分别是ObservableMap,ObservableConcatMap和ObservableFromArray。其中ObservableFromArray我理解是数据发射源(链表头),当我们在最外层ObservableMap(链表尾)调用subscribe注册建立订阅关系时,调用了ObservableMap实现了subscribeActual的方法。可以看到该方法内部通过source再去注册。在这个例子中此时source是ObservableConcatMap对象。
public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> {
protected final ObservableSource<T> source;
...
@Override
public void subscribeActual(Observer<? super U> t) {
source.subscribe(new MapObserver<T, U>(t, function));
}
...
}
再来看ObservableConcatMap类的subscribeActual方法,忽略掉暂时不关系的代码,可以看到这里也是调用subscribe再一次进行注册,那个时候的source对象是ObservableFromArray的实例。
public final class ObservableConcatMap<T, U> extends AbstractObservableWithUpstream<T, U> {
...
@Override
public void subscribeActual(Observer<? super U> observer) {
...
if (delayErrors == ErrorMode.IMMEDIATE) {
SerializedObserver<U> serial = new SerializedObserver<>(observer);
source.subscribe(new SourceObserver<>(serial, mapper, bufferSize));
} else {
source.subscribe(new ConcatMapDelayErrorObserver<>(observer, mapper, bufferSize, delayErrors == ErrorMode.END));
}
}
...
}
其中ObservableFromArray的subscribeActual方法在前面已经分析过了。其中这里就都走到的链式调用的(链表头部)。接下载就是一步一步数据返回的操作的。数据从链表头部返回到链表尾部。整个注册的过程是不是就像一个套娃,一个接着一个的注册,理论上这个双向链表有多长就会注册多少个,没有上限的,可以根据业务来调整。但是一般的开发中4-5个节点已经算个比较多的,就算加上线程切换的两个节点。
上述过程中不知道大家有没有发现设计模式的影子。当我们通过ObservableMap注册时候,并不是ObservableMap直接处理的,而是通过ObservableConcatMap,ObservableConcatMap也是同理,是通过ObservableFromArray来处理,这种我理解为是一种【代理模式】,代理模式介绍可以访问【设计模式】代理模式(Proxy Pattern)。
其中Observer也是,我们最开始注册的时候的Observer被MapObserver持有
而ObservableMap又通过MapObserver实例对象去建立订阅关系;
在ObservableConcatMap中MapObserver实例对象有被SourceObserver或者ConcatMapDelayErrorObserver持有,ObservableConcatMap中又通过SourceObserver或者ConcatMapDelayErrorObserver去建立订阅关系
这一层一层的关系也是一种代理模式
可以理解为每个节点Node,都持有上一个节点的Observable,头结点除外,并且持有下一个节点的Observer,尾结点除外。
可以理解为每个节点Node,都持有上一个节点的Observable,头结点除外,并且持有下一个节点的Observer,尾结点除外。
可以理解为每个节点Node,都持有上一个节点的Observable,头结点除外,并且持有下一个节点的Observer,尾结点除外。
我觉的这句话很重要说三遍,希望读者可以多品一品。
根据上面的demo,画了一个逻辑结构图,注册的时候是从:
ObservableFromArray <-- ObservableConcatMap <-- ObservableMap
观察者的回调是从:
ConcatMapDelayErrorObserver --> MapObserver --> DiyObserver
综上所述,是RxJava大概的逻辑结构,至于读者想要了解每一个具体的操作符怎么实现的,具体原理,可以在此基础上,对没有双向链表中的节点做具体的分析。当然本人也会去分析他们,有机会分享不同的操作符具体的实现。
大家共同成长,加油,打工人!!!
大家共同成长,加油,打工人!!!
大家共同成长,加油,打工人!!!