目錄
1.RxJava 3
JavaDoc
http://reactivex.io/RxJava/3.x/javadoc/
https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0
添加依賴
implementation 'io.reactivex.rxjava3:rxjava:3.x.y'
implementation 'io.reactivex.rxjava3:rxandroid:3.x.y'
Java 8(來源於官方文檔)
由於Android運行時支持方面的落後,RxJava 長期基於Java 6 API。未來的 Android Studio 4中,一個叫做 desuging 的過程能夠將許多Java 7和8的特性,透明地轉換成與 Java 6兼容的特性。
So,RxJava3的baseline可以提高到 Java 8,並增加Java 8的官方支持,比如:Optional、Stream等,因此必須將項目的編譯目標設置更改爲 java8:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
RxJava 3.0遵循Reactive-Streams specification規範。
2.觀察者模式組合
在RxJava 3中,提供了五對觀察者模式組合,每一對組合依靠其可調用的一系列函數的差異,具有各自的特點。
第一組:Observable(ObservableSource)/ Observer
一次可發送單條數據或者數據序列onNext,可發送完成通知onComplete或異常通知onError,不支持背壓。
第二組:Publisher(Flowable)/ Subscriber(FlowableSubscriber)
第一組基礎上進行改進,發送0到N個的數據(onNext),支持Reactive-Streams和背壓,可發送完成通知onComplete或異常通知onError,但效率沒有第一組高。
第三組:Single(SingleSource) / SingleObserver
第一組簡化版,只能發送單條數據onSuccess,或者異常通知onError
第四組:Completable(CompletableSource)/ CompletableObserver
第一組簡化版,不能發送數據,只發送完成通知onComplete或者異常通知onError
第五組:Maybe(MaybeSource) / MaybeObserver
第三,第四組的合併版,只能發送單條數據onSuccess和完成通知onComplete或者發送一條異常通知onError
3.Flowable(數據源類一)
3.1.Flowable
Flowable是RxJava2.x中新增的,專門用於應對背壓(Backpressure)問題,在Observable的基礎上優化後的產物。
Observable ——> subscribe() ——> Observer
Flowable ——> subscribe() ——> Subscriber
後者是前者針對背壓問題的優化產物,前者運行效率更高,後者支持背壓問題處理,但邏輯更多,運行效率稍低,速度稍慢。
Flowable數據流程:Flowable ——> 異步緩存池(128)——> Subscriber
Flowable默認異步緩存隊列大小爲128。作用是緩存當前未來得及處理(Subscriber未接收,或者由於某種原因,Flowable未實際發送給Subscriber)的數據。也就是說隨着Subscriber對數據的接收處理,緩存池也會隨之做清理更新:去掉已經被Subscriber接收的,塞進Flowable新發送的。
注意:並不是Subscriber接收一條,便清理一條,而是存在一個延遲,等累積一段時間後統一清理一次。
所以:當生產者和消費者在相同的線程中;或者當生產者和消費者在不同線程中,但消費者消費數據的速度不低於生產者發送數據的速度,或者整條數據流中只有一條數據時 …… 使用Observable會更好。
Flowable
.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> e) throws Exception {
System.out.println("發送----> 1");
e.onNext(1);
System.out.println("發送----> 2");
e.onNext(2);
System.out.println("發送----> 3");
e.onNext(3);
System.out.println("發送----> 完成");
e.onComplete();
}
}, BackpressureStrategy.BUFFER) //指定背壓處理策略:緩存
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
s.request(Long.MAX_VALUE); // 響應式拉取
}
@Override
public void onNext(Integer integer) {
System.out.println("接收----> " + integer);
}
@Override
public void onError(Throwable t) {}
@Override
public void onComplete() {
System.out.println("接收----> 完成");
}
});
注意:
(1)BackpressureStrategy.BUFFER設置背壓處理策略
(2)Subscription區別於Observer的Disposable
(3)發送器FlowableEmitter,不同於Observable的ObservableEmitter
3.2.BackpressureStrategy背壓策略
Flowable本身適用於異步場景,內置默認上限128的異步緩存池,用於緩存當前未來得及處理(Subscriber未接收或者由於某種原因Flowable未實際發送給Subscriber)的數據。BackpressureStrategy的作用便是用來設置Flowable通過異步緩存池緩存數據的策略。
RxJava 3針對不同的五種策略(MISSING,ERROR,DROP,LATEST,BUFFER)內置了相應的數據發送器類,系統通過代理模式對五種數據發送器做了包裝(FlowableCreate.java中實現)。
public void subscribeActual(Subscriber<? super T> t) {
BaseEmitter<T> emitter;
switch (backpressure) {
case MISSING: {
emitter = new MissingEmitter<T>(t);
break;
}
case ERROR: {
emitter = new ErrorAsyncEmitter<T>(t);
break;
}
case DROP: {
emitter = new DropAsyncEmitter<T>(t);
break;
}
case LATEST: {
emitter = new LatestAsyncEmitter<T>(t);
break;
}
default: {
emitter = new BufferAsyncEmitter<T>(t, bufferSize());
break;
}
}
t.onSubscribe(emitter);
// ......
}
MISSING(MissingEmitter<T>)
策略:無特定背壓處理策略,對數據無緩存和丟棄的處理,需結合創建背壓操作符確定背壓策略。
背壓操作符:
onBackpressureBuffer() 對應BackpressureStrategy.BUFFER
onBackpressureDrop() 對應BackpressureStrategy.DROP
onBackpressureLatest() 對應BackpressureStrategy.LATEST
static final class MissingEmitter<T> extends FlowableCreate.BaseEmitter<T> {
// ......
@Override
public void onNext(T t) {
if (isCancelled()) {
return;
}
if (t != null) {
actual.onNext(t);
} else {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
for (; ; ) {
long r = get();
if (r == 0L || compareAndSet(r, r - 1)) {
return;
}
}
}
}
ERROR(ErrorAsyncEmitter<T>)
策略:Flowable異步緩存池超限(onOverflow()),直接扔異常MissingBackpressureException,緩存池上限128。
static final class ErrorAsyncEmitter<T> extends NoOverflowBaseAsyncEmitter<T> {
private static final long serialVersionUID = 338953216916120960L;
ErrorAsyncEmitter(Subscriber<? super T> actual) {
super(actual);
}
@Override
void onOverflow() {
onError(new MissingBackpressureException("create: could not emit value due to lack of requests"));
}
}
DROP(DropAsyncEmitter<T>)
策略:Flowable的異步緩存池滿了,直接丟掉後續發送的數據。
static final class DropAsyncEmitter<T> extends NoOverflowBaseAsyncEmitter<T> {
private static final long serialVersionUID = 8360058422307496563L;
DropAsyncEmitter(Subscriber<? super T> actual) {
super(actual);
}
@Override
void onOverflow() {// nothing to do}
}
onOverflow()方法 do nothing!很明顯處理邏輯在其父類NoOverflowBaseAsyncEmitter,查看代碼邏輯,發現在調用actual.onNext(t);之前做了get() != 0的判斷,意思就是說緩存超限,什麼也不做,不超限才發送數據。也就是說如果Flowable的異步緩存池滿了,會丟掉後續由上層發送的數據(FlowableEmitter<Integer> e onNext() 發送了,但是底層actual.onNext(t);實際並未調用)
@Override
public final void onNext(T t) {
if (isCancelled()) {
return;
}
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
if (get() != 0) {
actual.onNext(t);
BackpressureHelper.produced(this, 1);
} else {
onOverflow();
}
}
LATEST(LatestAsyncEmitter<T>)
策略:Flowable的異步緩存池滿了,直接丟掉後續發送的數據。但會保證最後一條數據強制緩存,並保證發送,通過內置final AtomicReference<T> queue; 實現。也就是說無論如何,Subscriber都會接收到Flowable發送的最後一條數據。
代碼比較複雜,分析略!
BUFFER(BufferAsyncEmitter<T>)
策略:通過內置
final SpscLinkedArrayQueue<T> queue;
緩存所有超限數據,該策略不會拋出MissingBackpressureException異常,不會丟失數據(在內置緩存大小範圍內),但會導致OOM。
SpscLinkedArrayQueue內部實現爲數組,大小默認128,當緩存數超過128,SpscLinkedArrayQueue會重新分配緩存空間,簡而言之,這是一個無限大的緩存池。
static final class BufferAsyncEmitter<T> extends BaseEmitter<T> {
// ......
final SpscLinkedArrayQueue<T> queue;
BufferAsyncEmitter(Subscriber<? super T> actual, int capacityHint) {
super(actual);
this.queue = new SpscLinkedArrayQueue<T>(capacityHint);
this.wip = new AtomicInteger();
}
@Override
public void onNext(T t) {
// ......
queue.offer(t);
// ......
}
// ......
}
3.3.響應式拉取(Flowable特有)
Subscription接口定義了兩個方法:
void cancel(); 取消Subscriber與Flowable的訂閱關係。
void request(long n); 聲明並控制訂閱者對Publisher數據的需求量。
Flowable在設計的時候,採用了一種新的思路——響應式拉取方式,來設置訂閱者對數據的請求數量,Flowable可以根據訂閱者的需求量,按需發送數據。
如果不顯式調用Subscription.request(Long)則默認訂閱者的需求量爲零,所以如果Subscriber不顯示調用request方法,Flowable發送的數據並不會交給Subscriber處理。
Subscription.request(Long)可多次調用,Subscriber總共獲取的數據量是多次調用累計之和(不大於Flowable發出的數據量),如果Subscriber請求的數據量小於Flowable發送的數據量,那麼需求之外的數據仍然放到了異步緩存池中。如果異步緩存池超限,會導致MissingBackpressureException異常。
當Subscription.request(0)時,報錯:java.lang.IllegalArgumentException: n > 0 required but it was 0
3.4.FlowableEmitter(數據發送器)
響應式拉取的初衷,是爲了設置訂閱者對數據的請求數量,Flowable可以根據訂閱者的需求量,按需發送數據。
FlowableEmitter可以實現這一需求,FlowableEmitter內置long requested();可以獲取當前Flowable未完成的請求數量。隨着Flowable不停發送數據,long requested();值會相應變化。
public interface FlowableEmitter<T> extends Emitter<T> {
void setDisposable(@Nullable Disposable s);
void setCancellable(@Nullable Cancellable c);
long requested();
boolean isCancelled();
@NonNull
FlowableEmitter<T> serialize();
@Experimental
boolean tryOnError(@NonNull Throwable t);
}
重點關注long requested();方法!
同步狀態下:
FlowableEmitter.requested();方法獲取到的最大未完成請求數 == Subscriber 中Subscription.request(Long)中請求的數量,與Flowable緩存隊列大小無關。
FlowableEmitter.requested();的實時值 = Subscription.request(Long)中請求的數量 – Flowable已經發送的數量。
異步狀態下:
FlowableEmitter.requested();方法獲取到的最大未完成請求數 == 128;等於Flowable緩存隊列大小,與Subscriber 中Subscription.request(Long)中請求多少無關。
Flowable有一個異步緩存池,Flowable發送的數據,先放到異步緩存池中,再由異步緩存池交給Subscriber。所以Flowable在發送數據時,首先需要考慮的不是Subscriber的數據請求量,而是緩存池中能不能放得下,放得下直接塞入,放不下執行背壓策略。至於是否超出了Subscriber的數據需求量,可以在緩存池向其傳遞數據時,再作判斷,如果未超出,則將緩存池中的數據傳遞,如果超出了,則不傳遞。
FlowableEmitter.requested();的實時值 = Flowable異步緩存池當前可用空間大小。並不是下游真正的數據請求數量!
隨着緩存池把數據發送給Subscriber,Flowable將執行清理緩存的策略,異步緩存池中的數據並不是向Subscriber發送一條便清理一條,而是每等累積到N條時,清理一次。通過e.requested()獲取到的值,正是在異步緩存池清理數據時回升的。也就是異步緩存池每次清理後,都會導致Flowable未完成請求數量的回升,理想狀態下,緩存池在Flowable發送數據時一直有空間,這樣既不會引發背壓異常,也不會導致數據遺失。
例如:
1.Flowable有N條數據(N ≤ 128),Subscription.request(M)(M < N)
Flowable實際發送N條數據到異步緩存池,再由異步緩存池發送M條數據給Subscriber。
2.Flowable有N條數據(N ≤ 128),Subscription.request(M)(M ≥ N)
Flowable實際發送N條數據到異步緩存池,再由異步緩存池發送N條數據給Subscriber。
3.Flowable有N條數據(N > 128),Subscription.request(M)(M ≤ 128)
Flowable實際發送K(N ≥ K ≥ 128,K的具體數量和Subscriber接收處理數據的速度有關)條數據到異步緩存池,其餘部分執行背壓策略,再由異步緩存池發送M條數據給Subscriber。
4.Flowable有N條數據(N > 128),Subscription.request(M)(M > 128)
Flowable實際發送K(N ≥ K ≥ 128,K的具體數量和Subscriber接收處理數據的速度有關)條數據到異步緩存池,其餘部分執行背壓策略,再由異步緩存池發送M(M < K) / K(M ≥ K)條數據給Subscriber。
注意:onComplete(),onError()並不減少requested值。
3.5.背壓問題解決方案:(Flowable按需發送數據)
如果由於訂閱者處理數據緩慢,導致Flowable異步緩存池被塞滿且無法得到清理,通過requested()獲取的值也就變成了0,會根據BackpressureStrategy背壓策略的不同,拋出MissingBackpressureException異常,或做額外緩存或者丟掉這條數據。所以Flowable只需要在e.requested()等於0時,暫停發送數據,便可解決背壓問題!!!
完整方案:
發送方按需發送;
@Override
public void subscribe(FlowableEmitter<Integer> e) throws Exception {
int i = 0;
while (true) {
if (e.requested() == 0) continue; //此處添加代碼,讓flowable按需發送數據
System.out.println("發送---->" + i);
i++;
e.onNext(i);
}
}
接收方處理一條再請求下一條;
private Subscription mSubscription;
@Override
public void onSubscribe(Subscription s) {
s.request(1); // 設置初始請求數據量爲1
mSubscription = s;
}
@Override
public void onNext(Integer integer) {
try {
Thread.sleep(50);
System.out.println("接收------>" + integer);
mSubscription.request(1);// 每接收到一條數據增加一條請求量
} catch (InterruptedException ignore) {
}
}
4.Observable(數據源類二)
Observable:抽象類Observable是接口ObservableSource的一個抽象實現,我們可以通過Observable創建一個可觀察對象發送數據流。
// 發送對應的方法
Observable.create(new ObservableOnSubscribe<String>() {
// 默認在主線程裏執行該方法
@Override
public void subscribe(@NonNull ObservableEmitter<String> e) throws Exception {
e.onNext("Hello");
e.onNext("World");
// 結束標識
e.onComplete();
}
});
ObservableEmitter:數據發送器
public interface Emitter<T> {
void onNext(@NonNull T value);
void onError(@NonNull Throwable error);
void onComplete();
}
(1)onNext:用來發送數據,可多次調用,每調用一次發送一條數據
(2)onError:用來發送異常通知,只發送一次,若多次調用只發送第一條
(3)onComplete:用來發送完成通知,只發送一次,若多次調用只發送第一條
onError與onComplete互斥,兩個方法只能調用一個不能同時調用!
接口Observer中的三個方法(onNext,onError,onComplete)正好與Emitter中的三個方法相對應,分別對Emitter中對應方法的行爲作出響應。
Emitter調用onNext發送數據時,Observer會通過onNext接收數據。
Emitter調用onError發送異常通知時,Observer會通過onError接收異常通知。
Emitter調用onComplete發送完成通知時,Observer會通過onComplete接收完成通知。
Observable 在2.0中被設計成了無處理背壓能力的,避免了1.0中出現的Observable不能合理背壓,導致無法預料的MissingBackpressureException出現。
參數對象ObservableOnSubscribe會被存儲在返回的 Observable 對象中,它的作用相當於一個計劃表,當 Observable 被訂閱的時候,ObservableOnSubscribe的 subscribe () 方法會自動被調用,事件序列就會依照設定依次觸發(ObservableEmitter將會被調用2次 onNext() 和1次 onComplete())。這樣,由被觀察者調用了觀察者的回調方法,就實現了由被觀察者向觀察者的事件傳遞,即觀察者模式。
4.1.Observable
通過Observable.create、Observable.interval等創建型操作符生成的Observable。
(1)當一個訂閱者訂閱Observable(包括重複訂閱)時,Observable會重新開始發送數據給訂閱者。
(2)當多個訂閱者訂閱到同一個Observable時,訂閱者收到的數據是相互獨立的。
(3)(2)情況下,當其中一個訂閱者取消訂閱,Observable只會停止對該訂閱者的數據發送。
4.2.ConnectableObservable
ConnectableObservable通過Observable.publish();Observable.reply(int N) 轉換生成。publish():訂閱者訂閱ConnectableObservable只能收到訂閱行爲以後數據源發送的數據;replay(int N):訂閱者訂閱ConnectableObservable能收到訂閱行爲之前發送的N個數據。
(1)無論ConnectableObservable有沒有訂閱者,只要調用了ConnectableObservable的connect方法,原始Observable就開始發送數據。
(2)connect返回一個Disposable對象,調用了該對象的dispose方法,原始Observable將會停止發送數據,所有ConnectableObservable的訂閱者也無法收到數據。
(3)在調用connect返回的Disposable對象後,如果重新調用了connect方法,那麼原始Observable會重新發送數據。
(4)當多個訂閱者訂閱同一個ConnectableObservable時,它們收到的數據是相同的。
(5)當一個訂閱者取消對ConnectableObservable,不會影響其他訂閱者收到消息。
(6)多個訂閱者訂閱ConnectableObservable,真正原始屬於源並不知道訂閱者的存在,和訂閱者交互的實際是ConnectableObservable,起到一個承上啓下的中介作用。
4.3.refObservable
refObservable指的是由ConnectableObservable通過.refCount() 轉換而來的Observable;或者由Observable.share()方法轉換成的Observable。
(1)當第一個訂閱者訂閱refObservable後,原始Observable開始發送數據。
(2)之後的訂閱者訂閱到refObservable後,只能接收到訂閱之後,原始Observable 發送的數據。
(3)(2)情況下,如果一個訂閱者取消訂閱refObservable,不影響其他訂閱者接收數據;如果全部訂閱者都取消了訂閱,則原始Observable停止發送數據。
4.4.autoObservable
autoObservable由ConnectableObservable通過.autoConnect(int N)轉換而來的特殊Observable:
(1)當有N個訂閱者訂閱autoObservable後,原始Observable開始發送數據。
(2)只要原始Observable開始發送數據,即使所有的訂閱者都取消了對autoObservable的訂閱,原始Observable也不會停止發送數據。
5.Observer and Subscriber
// 創建被觀察者
Observable.just("Hello", "World")
// 將被觀察者切換到子線程
.subscribeOn(Schedulers.io())
// 將觀察者切換到主線程
.observeOn(AndroidSchedulers.mainThread())
// 創建觀察者並訂閱
.subscribe(new Observer<String>() {
// Disposable 相當於RxJava1.x中的 Subscription,用於解除訂閱
private Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
@Override
public void onNext(String s) {
Log.i("JAVA", "被觀察者向觀察者發送的數據:" + s);
if (s == "-1") { // "-1" 時爲異常數據,解除訂閱
disposable.dispose();
}
}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
訂閱做了以下幾件件事:
(1)首先調用onSubscribe()方法
(2)其次調用ObservableOnSubscribe的subscribe方法,事件發送的邏輯由這裏開始運行,之後針對性地調用Observer相應方法進行數據響應。
另外,直接訂閱或傳入消費者會產生一個新的類:Disposable,用於取消訂閱。Observable在訂閱取消之後發送的數據,Observer將不會接收。
簡化訂閱:上面Observer可以通過Consumer(Consumer就是消費者的意思,替代1.0的Action0,Action1),來消費onSubscribe,onNext,onComplete,onError等事件:
RxJava 3中沒有了一系列的Action/Func接口,取而代之的是與Java8命名類似的函數式接口。Action類似於RxJava1.x中的Action0,區別在於Action允許拋出異常;Consumer即消費者,用於接收單個值,BiConsumer則是接收兩個值,Function用於變換對象Function的泛型第一個爲接收參數的數據類型,第二個爲轉換後要發送的數據類型,Predicate用於判斷。這些接口命名大多參照了Java8。
Observable.just("Hello", "World")
.subscribe(new Consumer<String>() {
@Override
public void accept(@NonNull String s) throws Exception {
//這裏接收數據(onNext)。
Log.i("JAVA", "被觀察者向觀察者發送的數據:" + s);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
//這裏接收異常Throwable。
}
}, new Action() {
@Override
public void run() throws Exception {
//這裏接收onComplete。
}
}, new Consumer<Disposable>() {
@Override
public void accept(@NonNull Disposable disposable) throws Exception {
//這裏相當於onSubscribe。
}
});