了解本章内容请最好是先了解
RxJava2操作符内部流程理解-概念
zip操作符就是把多个数据源一起接收做处理,然后转换为一个新的数据源往下继续传递
期间只要是有一个数据源没有发送数据,此操作符都不会进行结果的处理。因为所需的数据不齐。我们来看看这个神奇的操作符是怎么处理的。
示例代码
Observable<Long> interval1 = Observable.interval(2, TimeUnit.MILLISECONDS);
Observable<Long> interval2 = Observable.interval(10, TimeUnit.SECONDS);
Observable.zip(interval1, interval2, new BiFunction<Long, Long, Long>() {
@Override
public Long apply(Long aLong, Long aLong2) throws Exception {
return aLong + aLong2;
}
}).subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
}
});
按照我们的思路,我们知道zip操作符会在Zipobserver里面执行对应的逻辑,这里我们点开zip操作符查看
最终会调用到这里
step1
public static <T1, T2, R> Observable<R> zip(
ObservableSource<? extends T1> source1, ObservableSource<? extends T2> source2,
BiFunction<? super T1, ? super T2, ? extends R> zipper) {
ObjectHelper.requireNonNull(source1, "source1 is null");
ObjectHelper.requireNonNull(source2, "source2 is null");
return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2);
}
这里 Functions.toFunction(zipper) 其实内部就是一个针对参数数量的封装,参数转换为一个object[]数组,再用object[0],object[1],object[2]依次传入apply方法。
我们主要看zipArray这个方法做了什么
step2
@SchedulerSupport(SchedulerSupport.NONE)
public static <T, R> Observable<R> zipArray(Function<? super Object[], ? extends R> zipper,
boolean delayError, int bufferSize, ObservableSource<? extends T>... sources) {
if (sources.length == 0) {
return empty();
}
ObjectHelper.requireNonNull(zipper, "zipper is null");
ObjectHelper.verifyPositive(bufferSize, "bufferSize");
return RxJavaPlugins.onAssembly(new ObservableZip<T, R>(sources, null, zipper, bufferSize, delayError));
}
果不其然,在经历过一系列的参数检查之后,还是到了我们熟悉的 ObservableZip 类里面了,我们猜测里面有一个ZipObserver的内部类,而且在 subscribeActual 方法里面肯定会有相关的订阅上下游的操作,继续往下面看。
step3
@Override
@SuppressWarnings("unchecked")
public void subscribeActual(Observer<? super R> observer) {
ObservableSource<? extends T>[] sources = this.sources;
int count = 0;
if (sources == null) {
sources = new Observable[8];
for (ObservableSource<? extends T> p : sourcesIterable) {
if (count == sources.length) {
ObservableSource<? extends T>[] b = new ObservableSource[count + (count >> 2)];
System.arraycopy(sources, 0, b, 0, count);
sources = b;
}
sources[count++] = p;
}
} else {
count = sources.length;
}
if (count == 0) {
EmptyDisposable.complete(observer);
return;
}
ZipCoordinator<T, R> zc = new ZipCoordinator<T, R>(observer, zipper, count, delayError);
zc.subscribe(sources, bufferSize);
}
这里传入的参数 observer 肯定是下游操作符里面的observer对象了,首先我们获取sources,什么?这个sources那里来了,前面第一章的时候我们分析这里面有一个source 是理所当然的因为这个上游的数据源嘛,这里为什么有一个sources数组???仔细查看才知道,哦,这里的sources就是我们step1里面的 可变长度参数列表里面的吗?哦,这个sources 就是我们的示例代码里面的两个定时器,这里我们的代码主要定位到下面的这个
ZipCoordinator类,看代码的意思是它订阅了所有的数据源,Coordinator这个单词的翻译是 n. 协调人;调度员; 配位仪; 共同调济器(官),我们猜测它肯定是做了管理source发送过来的数据源的事。继续往下
step4
public void subscribe(ObservableSource<? extends T>[] sources, int bufferSize) {
ZipObserver<T, R>[] s = observers;
int len = s.length;
for (int i = 0; i < len; i++) {
s[i] = new ZipObserver<T, R>(this, bufferSize);
}
// this makes sure the contents of the observers array is visible
this.lazySet(0);
downstream.onSubscribe(this);
for (int i = 0; i < len; i++) {
if (cancelled) {
return;
}
sources[i].subscribe(s[i]);
}
}
看到这里和我们想的查差不多,获取sources 的长度len,再创建len个ZipObserver对象,然后每个source都订阅一个ZipObserver,这里我们可以知道,数据源发过来有地方接收到了,而且我们发现 ZipObserver 对象的构造方法都传入了这同一个ZipCoordinator 实例,接下来我们去看 ZipObserver 怎么把这些数据处理起来的
step5
ZipObserver(ZipCoordinator<T, R> parent, int bufferSize) {
this.parent = parent;
this.queue = new SpscLinkedArrayQueue<T>(bufferSize);
}
从代码我们看到,都创建了一个queue
public void onNext(T t) {
queue.offer(t);
parent.drain();
}
当数据过来的时候先放入这个queue里面去,然后调用parent的drain方法,又回到我们的协调人 ZipCoordinator 对象里面去了
public void drain() {
if (getAndIncrement() != 0) {
return;
}
int missing = 1;
final ZipObserver<T, R>[] zs = observers;
final Observer<? super R> a = downstream;
final T[] os = row;
final boolean delayError = this.delayError;
for (;;) {
for (;;) {
int i = 0;
int emptyCount = 0;
for (ZipObserver<T, R> z : zs) {
// 这里保证os[]没有存储的数据才再次去差找,否则会被连续poll删除造成数据错误
if (os[i] == null) {
boolean d = z.done;
T v = z.queue.poll();
boolean empty = v == null;
if (checkTerminated(d, empty, a, delayError, z)) {
return;
}
if (!empty) {
//这里只要不为空就把数据保存到os[]数组里面
os[i] = v;
} else {
emptyCount++;
}
} else {
if (z.done && !delayError) {
Throwable ex = z.error;
if (ex != null) {
cancel();
a.onError(ex);
return;
}
}
}
i++;
}
if (emptyCount != 0) {
break;
}
R v;
try {
v = ObjectHelper.requireNonNull(zipper.apply(os.clone()), "The zipper returned a null value");
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
cancel();
a.onError(ex);
return;
}
a.onNext(v);
Arrays.fill(os, null);
}
missing = addAndGet(-missing);
if (missing == 0) {
return;
}
}
}
核心出现了,看到了吗?遍历所有的zipobserver对象,从中不断的查找os[i] == null 的index,这样去对应的observer 里面去poll数据,从中的queue获取数据v,判断v是否为空 empty 然后往下
if (checkTerminated(d, empty, a, delayError, z)) {
return;
}
checkTerminated(d, empty, a, delayError, z) 方法检查是否完成,参数d是指的done,这里为false,所以代码不会进入这个条件判断
然后
if (!empty) {
os[i] = v;
} else {
emptyCount++;
}
这里如果不为空,就保存数据到os[i]里面,否则就计数空值的次数,就这样每次去获取数据然后填充到这个os[]里面,直到下面的没有空的结果
if (emptyCount != 0) {
break;
}
R v;
try {
v = ObjectHelper.requireNonNull(zipper.apply(os.clone()), "The zipper returned a null value");
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
cancel();
a.onError(ex);
return;
}
a.onNext(v);
这里就快迎来大结局了,看到了吗?当没有空结果的时候,走到zipper.apply(os.clone)还记得我们step1里面写的参数数组吗?没错这里就调用到我们自己传入的 BiFunction()里面了,最终把结果继续往下传递。
zip操作符的核心思想
用一个ZipCoordinator管理多个ZipObserver,每个ZipObserver用于监听每个数据源r,每个ZipObserver接收到数据之后触发ZipCoordinator来drain(),读取每个 ZipObserver 里面的存放在queue里面的数据,等全部ZipObserver都收到数据之后,再调用zipper对象来处理转换,接着再往下传递。