文中的源代码版本为v2.2.0
RxJava之Connectable Observable
先总结
- 重复订阅的实现?
使用一个PublishObserver保存所有通过subscribe方法进行订阅的订阅者。再由PublishObserver订阅原始的Observable,PublishObserver接收到消息之后再依次派发给其保存的订阅者。- 如何控制消息在
connect方法调用之后才开始发送?
只有在调用源Observable的subscribe方法之后,消息才会真正开始发送。Connectable Observable正是利用了这一点,将subscribe方法的调用放在了connect方法中,实现了控制消息发送时机的功能。
Connectable Observable区别于普通Observable的地方在于
- 可以被重复订阅
- 被订阅之后并不会马上开始发送消息,只有在调用了
Connect操作符之后才会开始发送消息
本文的目的就是想通过阅读源码的形式去了解Connectable Observable的上述功能是如何实现的。
通过调用Publish操作符可以将普通的Observable转化为Connectable Observable。Observable.publish函数非常简单,内部调用了ObservablePublish.create(this),我们直接贴该函数的代码
ObservablePublish.create
create的主要工作:
- 创建一个
PublishObserver的AtomicReference对象。PublishObserver实现了Observer接口,我们可以将它看做是一个观察者 - 创建一个
PublishSource对象 - 创建并返回一个
ObservablePublish对象。ObservablePublish继承自ConnectableObservable
//ObservablePublish.java
public static <T> ConnectableObservable<T> create(ObservableSource<T> source) {
final AtomicReference<PublishObserver<T>> curr = new AtomicReference<PublishObserver<T>>();
ObservableSource<T> onSubscribe = new PublishSource<T>(curr);
return new ObservablePublish<T>(onSubscribe, source, curr);
}
PublishSource(AtomicReference<PublishObserver<T>> curr) {
this.curr = curr;
}
private ObservablePublish(ObservableSource<T> onSubscribe, ObservableSource<T> source,
final AtomicReference<PublishObserver<T>> current) {
this.onSubscribe = onSubscribe;
this.source = source;
this.current = current;
}
Connectable Observable创建完毕,我们接着看看subscribe函数里面做了什么。我们知道subscribe最终会调用subscribeActual。
ObservablePublish.subscribeActual
protected void subscribeActual(Observer<? super T> observer) {
onSubscribe.subscribe(observer);
}
该方法只有一句代码,onSubscribe的类型是PublishSource,我们继续跟下去
PublishSource.onSubscribe
public void subscribe(Observer<? super T> child) {
// create the backpressure-managing producer for this child
InnerDisposable<T> inner = new InnerDisposable<T>(child);
child.onSubscribe(inner);
// concurrent connection/disconnection may change the state,
// we loop to be atomic while the child subscribes
for (;;) {
// get the current subscriber-to-source
PublishObserver<T> r = curr.get();
// if there isn't one or it is disposed
if (r == null || r.isDisposed()) {
// create a new subscriber to source
PublishObserver<T> u = new PublishObserver<T>(curr);
// let's try setting it as the current subscriber-to-source
if (!curr.compareAndSet(r, u)) {
// didn't work, maybe someone else did it or the current subscriber
// to source has just finished
continue;
}
// we won, let's use it going onwards
r = u;
}
/*
* Try adding it to the current subscriber-to-source, add is atomic in respect
* to other adds and the termination of the subscriber-to-source.
*/
if (r.add(inner)) {
inner.setParent(r);
break; // NOPMD
}
}
}
//PublishObserver.java
final AtomicReference<InnerDisposable<T>[]> observers;
boolean add(InnerDisposable<T> producer) {
// the state can change so we do a CAS loop to achieve atomicity
for (;;) {
// get the current producer array
InnerDisposable<T>[] c = observers.get();
// if this subscriber-to-source reached a terminal state by receiving
// an onError or onComplete, just refuse to add the new producer
if (c == TERMINATED) {
return false;
}
// we perform a copy-on-write logic
int len = c.length;
@SuppressWarnings("unchecked")
InnerDisposable<T>[] u = new InnerDisposable[len + 1];
System.arraycopy(c, 0, u, 0, len);
u[len] = producer;
// try setting the observers array
if (observers.compareAndSet(c, u)) {
return true;
}
// if failed, some other operation succeeded (another add, remove or termination)
// so retry
}
}
主要干了两件事情:
- 创建一个
PublishObserver对象,并保存起来 - 将
subscribe函数传递进来的Observer包装成InnerDisposable对象添加到PublishObserver中。PublishObserver使用一个数组来保存InnerDisposable。
代码中的for循环以及一些AtomicReference的操作,我们可以不用太理会,主要是用来解决并发问题,于流程并没有太大影响。
好了,现在我们可以总结出两个事情
- 调用
Connectable Observable的subscribe函数并不会触发消息的发送,原因是它并没有调用源Observable的subscribe函数 - 通过
Connectable Observable的subscribe函数传递进来的Observer都会被保存在一个PublishObserver中。
消息的订阅已经完成,那么下面就是发送消息了,Connectable Observable只有在调用了connect方法之后才会开始发送消息,我们接着看
ConnectableObservable.connect
connect方法最终会进入到下面这个重载的connect方法中
public void connect(Consumer<? super Disposable> connection) {
boolean doConnect;
PublishObserver<T> ps;
// we loop because concurrent connect/disconnect and termination may change the state
for (;;) {
// retrieve the current subscriber-to-source instance
ps = current.get();
//这个if语句的核心逻辑就是创建一个PublishObserver对象
//如果之前掉用过subscribe方法,那么此处PublishObserver
//是不为null的
if (ps == null || ps.isDisposed()) {
// create a new subscriber-to-source
PublishObserver<T> u = new PublishObserver<T>(current);
// try setting it as the current subscriber-to-source
if (!current.compareAndSet(ps, u)) {
// did not work, perhaps a new subscriber arrived
// and created a new subscriber-to-source as well, retry
continue;
}
ps = u;
}
//shouldConnect为AtomicBoolean类型,确保并发场景下只触发一次
doConnect = !ps.shouldConnect.get() && ps.shouldConnect.compareAndSet(false, true);
break; // NOPMD
}
//...
if (doConnect) {
source.subscribe(ps);
}
}
//PublishObserver.java
public void onNext(T t) {
for (InnerDisposable<T> inner : observers.get()) {
inner.child.onNext(t);
}
}
可以看到,connect方法最最核心的功能就是调用原始Observable的subscribe方法,订阅者是PublishObserver。
再看PublishObserver.onNext方法,PublishObserver在接收到消息之后会遍历内部的数组,将消息挨个发送给之前保存起来的Observer