RxJava2 中 Flowable (其他被观察者,如 Observable 也提供了join()方法)提供了实例方法join().
示例代码如下:
Flowable<String> flowable1 = Flowable.interval(1, TimeUnit.SECONDS)
.map(aLong -> "L" + aLong);
Flowable<String> flowable2 = Flowable.interval(1, TimeUnit.SECONDS)
.map(aLong -> "R" + aLong);
Flowable flowable = flowable1.join(
flowable2,
i -> Flowable.timer(3, TimeUnit.SECONDS),
i -> Flowable.timer(6, TimeUnit.SECONDS),
(l, r) -> l + " - " + r)
.take(100);
flowable.subscribe(System.out::println);
flowable对象所发出的数据是第四个参数的方法体的返回值, 第四个参数中的 l 是flowable1发出的数据项, r是flowable2发出的数据项, 具体哪个l 或者 r会参与到第四个参数的方法逻辑中, 由第二个参数和第三个参数定义的时间窗口大小决定.
方法签名如下:
@CheckReturnValue
@NonNull
@BackpressureSupport(BackpressureKind.ERROR)
@SchedulerSupport(SchedulerSupport.NONE)
public final <TRight, TLeftEnd, TRightEnd, R> Flowable<R> join(
Publisher<? extends TRight> other,
Function<? super T, ? extends Publisher<TLeftEnd>> leftEnd,
Function<? super TRight, ? extends Publisher<TRightEnd>> rightEnd,
BiFunction<? super T, ? super TRight, ? extends R> resultSelector) {}
Flowable实例方法join()方法签名中包含四个参数:
- 第一个参数: 参与合并的另外一个
Flowable; - 第二个参数:
join调用者对应的时间窗口定义; - 第三个参数: 第二个参数other对应的时间窗口定义;
- 第四个参数: join()方法返回的Flowable发出的数据项在这个参数的方法体内编写;
示例分析:
join()调用者我们起个名字叫做 left, join()第一个参数也起个名字,叫做 right.
left.join(right,第二个参数, 第三个参数, 第四个参数);

代码运行输入如下:
L0 - R0
L1 - R0
L0 - R1
L1 - R1
L2 - R0
L2 - R1
L0 - R2
L1 - R2
L2 - R2
L3 - R0
L3 - R1
L3 - R2
L0 - R3
L1 - R3
L2 - R3
L3 - R3
L4 - R0
L4 - R1
L4 - R2
L4 - R3
L2 - R4
L3 - R4
L4 - R4
L5 - R0
L5 - R1
L5 - R2
L5 - R3
L5 - R4
L3 - R5
L4 - R5
L5 - R5
L6 - R0
L6 - R1
L6 - R2
L6 - R3
L6 - R4
L6 - R5
L4 - R6
L5 - R6
L6 - R6
L7 - R1
L7 - R2
L7 - R3
L7 - R4
L7 - R5
L7 - R6
L5 - R7
L6 - R7
L7 - R7
L5 - R8
L6 - R8
L7 - R8
L8 - R2
L8 - R3
L8 - R4
L8 - R5
L8 - R6
L8 - R7
L8 - R8
......
蓝色部分表示left, 桔色部分表地right,
left每一秒钟发送一个字符串, right 每一秒钟发送一个字符串, left的时间窗口大小为3秒钟, right的时间窗口大小为6秒钟,当join()方法返回的flowable调用subscribe方法后, left 和 right就开始发送数据了.
left 每发送一个数据, 这个数据会和当前时刻right中对应的窗口中发送过的数据结合. right所使用的时间窗口大小由第三个参数定义.
同样, right也执行同样的过程, 每发送一个数据, 这个数据会和当前时刻left中对应的窗口中发送过的数据结合, left所使用的时间窗口大小由第二个参数定义.
-
第1秒开始, left先发出L0, 此刻, 去查看right当前窗口内发送的数据时, right还没有发送, 所以不做处理. 然后, right发出R0, 会和此刻left窗口内发送的L0结合, 得到 L0 - R0.
-
第2秒开始, left发出L1, 以left角度来看, right的时间窗口大小为6秒, 当前right的时间窗口内发出的数据为R0 , L1和R0结合, 得到L1 - R0.
right发出R1, 以right角度来看, left的时间窗口大小为3秒, 当前left的时间窗口内发出的数据为L0, L1, 那要让R1和L0, L1结合, 得到 L0 - R1, L1 - R1. 从两个角度出发所得到的数据集是: L1 - R0, L0 - R1, L1 - R1. -
第3秒开始, left发出L2, 当前right窗口内发送的数据为R0, R1, 结合得到L2 - R0, L2 - R1. right发出R2, 当前left窗口内发送的数据为L0, L1, L2, 结合得到 L0 - R2, L1 - R2, L2 - R2.
-
第4秒开始, left发出L3, 当前right窗口内发送的数据为R0, R1, R2, 结合得到L3 - R0, L3 - R1, L3 - R2. right发出R3, 当前left窗口内发送的数据为L0, L1, L2, L3, 结合得到L0 - R3, L1 - R3, L2 - R3, L3 - R3.
-
第5秒开始, Left发出L4, 当前right窗口内发送的数据为R0, R1, R2, R3, 结合得到L4 - R0, L4 - R1, L4 - R2, L4 - R3. right发出R4, 当前left窗口时间内发送的数据为L2, L3, L4, 结合得到L2 - R4, L3 - R4, L4 - R4.
-
......
上面第5秒处, right发出R4时, 当前left的窗口大小为3秒, 按照常理往前数, 和R4结合的应该是L1,L2,L3,L4, 这个地方是有些迷惑的, 为什么不是L1,L2,L3,L4呢? 应该是这样的, 假定right发出R4的时间为TR4, 这时就要看在 (TR4 - 3秒)(left的窗口大小) 这个时间内left发出的数据项有哪些, 得到的left发出的数据只有L2,L3,L4. 所以就是上面的输出情况了.
执行上面的代码, 做多次测试, 第5秒的输出内容有时候是这样的:
L4 - R0
L4 - R1
L4 - R2
L4 - R3
L2 - R4
L3 - R4
L4 - R4
有时候是另外一种情况:
L4 - R0
L4 - R1
L4 - R2
L4 - R3
L1 - R4
L2 - R4
L3 - R4
L4 - R4
这其实就是要看R4发出的那一刻之前的3秒钟内left发出了哪些数据, 有可能包含L1, 也有可能不包含L1, 取决于left发送数据代码逻辑被执行的快慢了.
综上
FLowable实例方法join结合两个Flowable发出的数据.
left 发出一个数据, 要和right在当前时刻 向前 的一个窗口时间(right的窗口时间)内发出的所有数据相结合. 同理, right也执行这样的过程, 每发送一个数据, 要和left在当前时刻 向前 的一个窗口时间(left的窗口时间)内发出的所有数据相结合.
left和right发出的数据的结合逻辑在join方法的第四个参数的方法体中实现.
纸上得来终觉浅, 绝知此事要躬行.
[陆游]