這篇文章離上一篇文章有些時日了,概因最難心情大大的不好,非常不爽。
為啥我會專門寫一下熱發射呢,因為 RxBus 就是使用 RxJava 的熱發射(Subject)實現的,但是呢我的出發點不同,我是因為研究了 AAC 的 LivaData 之后才有感而發的,同樣是響應式編程,我們如何使用 RxJava 實現構建數據和 UI 之間的通道,做到動態更新數據呢
先來回顧一下 LivaData 的使用,這是介紹 AAC 組件:Android Architecture Components 開發架構 中 demo 的數據流程圖
UI 層拿到 modle層拋出的 livedata(Observable) ,在其上注冊更新 UI 的方法,然后 modle層在更新數據的時候直接調用 livedata.setValue 方法就可以更新 UI 層的數據了
這其實就是 RxJava 中熱發射的典型使用,所以我們真的有必要去學習一下了
先來概念
有熱必然有冷,那何為熱,何為冷,這點很重要的:
- 冷發射 寫
Observable 在收到 Observer 注冊時就發送數據,此為冷,我們平時使用的 RxJava 方式都是冷發射 - 熱發射
Observable 在收到 Observer 注冊時只是建立了相互關系,可以稱之為管道。之后 Observable 可以在需要的任何時候發射任意數據。
對于概念我覺得解釋的比較明白的是這篇文章里的:Learn RxJava 2 Observables 與 Subscribers
細細品鑒
冷發射經典應用如下
Observable.just(1, 2, 3)
.subscribe(integer -> {
......
});
- 冷發射的數據固定,要不是固定的數據,要不就是一個固定的從遠程 IP 獲取的數據,是相對寫死的
- 冷發射在注冊時即開始發射數據,我們不能決定發射數據的時機和地點
熱發射經典應用如下
// 創建熱發射 Observable
PublishSubject<String> subject = PublishSubject.create();
Disposable disposable = subject.subscribe(s -> show(s));
// 動態發送數據
subject.onNext("響應式編程");
// 中斷管道,接觸注冊關系
disposable.dispose();
- 熱發射我們可以決定發射時機,地點,數據,靈活可以實現和 livadata 相同的效果
- 可以解除單個注冊
熱發射核心 Subject
Subject 這個東西既可以當 Observable 用,上面我們已經看到了,也可以當 Observer 用,接受別的 Observable 的數據
我們先來看看 Subject 的幾個實現類,然后才好往下繼續
- PublishSubject
- 該Subject不會改變事件的發送順序。
- 如果在已經發送了一部分事件之后注冊的observer,
- 是不會收到之前發送的事件。
private void doPublishSubject() {
//將事件發送到observer,如果先前已經漏掉的事件,不會重新發送到后注冊的observer上
PublishSubject<String> publish = PublishSubject.create();
publish.subscribe(new PublishObserver<String>("first"));
publish.onNext("1");
publish.onNext("2");
publish.subscribe(new PublishObserver<String>("seconde"));
publish.onNext("3");
publish.onCompleted();
}
- BehaviorSubject
- 該類有創建時需要一個默認參數,該默認參數會在subject未發送過其他的事件時,向注冊的observer發送。
- 注意看代碼注釋
private void doBehaviorSubject() {
//將事件發送到observer,如果先前已經漏掉的事件,除了最近的一個事件以外,
//其他相關事件不會重新發送到后注冊的observer上。所以需要帶默認值,
//第一次被observer注冊時,observable中沒有內容的時候,就會將默認值發給observer
BehaviorSubject<String> behavior = BehaviorSubject.create("創建beahavior時候帶的消息");
behavior.subscribe(new SubjectObserver<String>("first"));
behavior.onNext("1");
behavior.onNext("2");
behavior.subscribe(new SubjectObserver<String>("seconde"));
behavior.onNext("3");
behavior.onCompleted();
}
- ReplaySubject
- 將事件發送到observer,無論什么時候注冊observer,
- 無論何時通過該observable發射的所有事件,均會發送給新的observer。
private void doReplaySubject() {
//將事件發送到observer,無論什么時候注冊observer,
//無論何時通過該observable發射的所有事件,均會發送給新的observer。
ReplaySubject<String> replay = ReplaySubject.create();
replay.subscribe(new SubjectObserver<String>("first"));
replay.onNext("1");
replay.onNext("2");
replay.subscribe(new SubjectObserver<String>("seconde"));
replay.onNext("3");
replay.onCompleted();
}
- AsyncSubject
- 只有當subject調用onComplete方法時,才會將subject中的最后一個事件傳遞給observer。
- 如果不調用onComplete方法,則不會給observer發送任何事件。
private void doAsyncSubject() {
//只會有當subject調用onComplete方法時,才會將subject中的最后一個事件傳遞給observer。
//如果不調用onComplete方法,則不會向observer中發送任何事件
AsyncSubject async = AsyncSubject.create();
async.subscribe(new SubjectObserver<String>("first"));
async.onNext("1");
async.onNext("2");
async.onNext("3");
async.onCompleted();
async.subscribe(new SubjectObserver<String>("seconde"));
async.onCompleted();
}
- Subject 作為Observer ,注冊到一個冷發射的 Observable 上面
- 注意此時我們只能使用 ReplaySubject
- 因為冷發射注冊既發射數據,所有這個 Subject 在注冊到冷發射的 Observable 上時就會接受這個冷發射的 Observable 的數據,然后繼續向下傳遞
ReplaySubject<String> subject = ReplaySubject.create();
subject.subscribe(s -> show(s));
Observable<String> observable = Observable.just("AA");
observable.subscribe(subject);
- Subject 作為Observer ,注冊到一個熱發射的 Observable 上面
- 這是既沒有類型限制了
- 我們添加一個變換進來,要不然沒啥意義,這個其實和 livadata 天劍 transform 一個思路
PublishSubject<String> subject1 = PublishSubject.create();
PublishSubject<String> subject2 = PublishSubject.create();
subject1.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
return s + "_經過2的修改了";
}
}).subscribe(subject2);
subject2.subscribe(s -> show(s));
subject1.onNext("響應式編程");
ConnnectableObservbale
我們可以把一個冷發射轉為熱發射,使用 publish
ConnectableObservable<String> source = Observable
.just("Alpha","Beta","Delta","Gamma","Epsilon")
.publish();
source.subscribe(s -> System.out.println("observer1 RECEIVED: " + s));
source.map(String::length)
.subscribe(i -> System.out.println("observer2 RECEIVED: " + i));
//發射!
source.connect();
不過我覺得這個 ConnnectableObservbale 沒啥發用,寫死數據的熱發射沒太大應用價值。
代碼特征
PublishSubject 我想大家肯定有一些疑問這里我測試過直接上答案
1. PublishSubject 變換不會丟失通道的特性,并且一樣可以多注冊
val subject = PublishSubject.create<String>()
val observable = subject.map {
return@map "BB"
}
observable.subscribe {
Log.d("AA", "收到數據1:$it")
}
observable.subscribe {
Log.d("AA", "收到數據2:$it")
}
subject.onNext("AA")
PublishSubject 變換之后雖然我們拿到的是 Observable,但是 PublishSubject 特性不會丟失,為啥?因為數據源頭不會像 Observable 一樣由注冊的就觸發數據,這個 Observable 的源頭還是 PublishSubject ,Observable 求到的只是銜接作用