前言
今天開始聊聊Rxjava,這個神奇又難用又牛逼的框架。
先說說Rxjava
兩個關鍵詞:
異步
。Rxjava可以通過鏈式調用隨意切換線程,同時又能保證代碼的簡潔。觀察者模式
。Rxjava的核心,說白了就是一個觀察者模式,通過觀察者訂閱被觀察者這一層訂閱關係來完成後續事件的發送等工作。
然後開始提問題了,Rxjava
涉及的內容很多,我還是會以三個問題爲單位,從易到難,一篇篇的說下去,今天的三問
是:
- RxJava的訂閱關係
- Observer處理完onComplete後會還能onNext嗎?
- RxJava中的操作符
RxJava的訂閱關係
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
emitter.onNext(1);
emitter.onComplete();
}
}).subscribe(new Observer<Integer>() {
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: " + integer);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
代碼中主要有三個角色:
-
被訂閱者Observable
,是整個事件的來源,可以發射數據給訂閱者。 -
訂閱者Observer
,通過subscribe方法和被訂閱者產生關係,也就是開始訂閱,同時可以接受被訂閱者發送的消息。 -
發射器Subscriber/Emitter
,在Rxjava2之後,發射器改爲了Emitter,他的作用主要是用來發射一系列事件的,比如next事件,complete事件等等。
有了這三個角色,一個完整的訂閱關係也就生成了。
Observer處理完onComplete後會還能onNext嗎?
要弄清楚這個問題,得去看看onComplete,onNext方法到底做了什麼。
@Override
public void onComplete() {
if (!isDisposed()) {
try {
observer.onComplete();
} finally {
dispose();
}
}
}
@Override
public void onNext(T t) {
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
if (!isDisposed()) {
observer.onNext(t);
}
}
public static boolean isDisposed(Disposable d) {
return d == DISPOSED;
}
public static boolean dispose(AtomicReference<Disposable> field) {
Disposable current = field.get();
Disposable d = DISPOSED;
if (current != d) {
current = field.getAndSet(d);
if (current != d) {
if (current != null) {
current.dispose();
}
return true;
}
}
return false;
}
源碼還是比較清晰明瞭,無論是onComplete
還是onNext
,都會判斷當前訂閱是否被取消,也就是Disposable類型的變量的引用是否等於DISPOSED
,如果等於則代表該訂閱已經被取消,起點和終點已經斷開聯繫。而在onComplete方法的結尾調用了dispose
方法,將原子引用類中的 Disposable
對象設置爲 DisposableHelper 內的 DISPOSED
枚舉實例,即斷開訂閱關係,所以在這之後所有的onNext,onComplete,onError
方法中的isDisposed判斷都不會通過,也就不會執行後續的數據發送等處理了。
RxJava中的操作符
- concatMap
- flatMap
這兩個操作符的功能是一樣的,都是將一個發射數據的Observable變換爲多個Observables,然後將它們發射的數據放進一個單獨的Observable。區別在於concatMap
是有序的,flatMap
是無序的,concatMap最終輸出的順序與原序列保持一致,而flatMap則不一定,有可能出現交錯。
舉個例子,發送數字01234,通過操作符對他們進行+1處理,發送2的時候進行一個延時:
Observable.fromArray(1,2,3,4,5)
.flatMap(new Function<Integer, ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> apply(@NonNull Integer integer) throws Exception {
int delay = 0;
if(integer == 2){
delay = 500;//延遲發射
}
return Observable.just(integer*10).delay(delay, TimeUnit.MILLISECONDS);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.e("jimu","accept:"+integer);
}
});
如上述操作,最終打印結果爲:10,20,40,50,30。因爲發送數字2的時候,進行了延時。
但是如果flatMap
操作符改成concatMap
,打印結果就是10,20,30,40,50了,這是因爲concatMap是有序的,會按照原序列的順序進行變換輸出。
- merge、concat、zip,合併
這幾個操作符是用作合併發射物的,可以將多個Obserable
和併成一個Obserable
:
Observable<Integer> odds=Observable.just(1,2,3,4);
Observable<Integer> events=Observable.just(5,6,7,8);
Observable.merge(odds,events).subscribe(i->Log.d("TAG","merge->"+i));
區別在於concat
操作符是在合併後按順序串行執行,merge
操作符是在合併後按時間線並行執行,如果出現某個數據進行延時發射,那麼結果序列就會發生變化。
而zip
操作符的特點是合併之後並行執行,發射事件和最少的一個相同,什麼意思呢?比如一個發送兩個數據的Obserable
和一個發射4條數據的Obserable
進行zip合併,那麼最終只會有兩條數據被髮射出來,看個例子:
Observable
.zip(Observable.just(1,2),Observable.just(3,4,5,6),new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(@NonNull Integer response, @NonNull Integer response2) throws Exception {
//將兩個發射器的結果相加
return response+response2;
}
})
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer s) throws Exception {
Log.e("lz","accept+"+s);
}
});
結果只會有兩條數據:4,6。第二個發射器發射的後面兩條數據會被拋棄。
- interval,週期執行
這個操作符主要用作定時週期任務,比如我需要每100ms發送一次數據:
Observable.interval(100, TimeUnit.MILLISECONDS)
.subscribe(new Observer<Long>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Long aLong) {
}
});
- timer,delay延遲發送數據
這兩個操作符都是用作延時發送數據,不同在於timer
是創建型操作符,而delay
是輔助型操作符。意思就是timer
操作符是可以直接創建一個Observable
,然後在訂閱之後延時發送數據項,看例子:
Observable
.timer(1000,TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.subscribe(disposableObserver);
而delay
是當原始的Observable
發送數據後,啓動一個定時器,然後延時將這個數據發送,所以它相當於是處在上游與下游之間的一個輔助項,用作延時發送,它的作用對象必須是個創建好的Observable
:
Observable
.just(0L)
.doOnNext(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
}
}
.timer(1000,TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.subscribe(disposableObserver);
最後
最後爲了幫助大家深刻理解Android相關知識點的原理以及面試相關知識,這裏放上相關的我搜集整理的24套騰訊、字節跳動、阿里、百度2019-2020BAT 面試真題解析,我把大廠面試中常被問到的技術點整理成了視頻和PDF(實際上比預期多花了不少精力),包知識脈絡 + 諸多細節。
還有 高級架構技術進階腦圖 幫助大家學習提升進階,也節省大家在網上搜索資料的時間來學習,也可以分享給身邊好友一起學習。
以上內容均放在了開源項目:【github】 中已收錄,裏面包含不同方向的自學Android路線、面試題集合/面經、及系列技術文章等,資源持續更新中...
當程序員容易,當一個優秀的程序員是需要不斷學習的,從初級程序員到高級程序員,從初級架構師到資深架構師,或者走向管理,從技術經理到技術總監,每個階段都需要掌握不同的能力。早早確定自己的職業方向,才能在工作和能力提升中甩開同齡人。