RxJava 火了挺久,一直沒怎麼去用,今天抱着學習的心態,一塊來研究下吧,看看能不能解決我們實際開發中的問題,不要爲了用而用。
在聽說RxJava的時候,肯定還聽說RxAndroid,這裏要先聲明一下,RxAndroid沒幾行代碼。主要是加了個Scheduler,AndroidSchedulers.mainThread()來方便在Android上使用RxJava。所以使用的時候,還是需要兩個都一起依賴的。
RxAndroid 的地址:https://github.com/ReactiveX/RxAndroid
RxJava的地址:https://github.com/ReactiveX/RxJava
1 ) 首先看下最新版本的可以點下Code,裏面有個releases
可以看到最新的是 2.1.14-RC1
2 ) 確定最新版本之後,就開始在gradle裏面添加依賴:
compile "io.reactivex.rxjava2:rxjava:2.1.14-RC1"
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
3 ) 環境準備好了,就來跑一下代碼。先看下官方的小示例:
Observable.just("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* an Observer */);
將這段代碼寫到我的頁面當中
運行成功,但是什麼效果都沒有。先來解釋一下這段代碼:
這個的原型實際上是用了觀察者模式,最簡單的觀察者模式就是給button通過setOnClickListener設置點擊事件。當用戶點擊button的時候,button通過之前設置的事件監聽,從而響應了OnClick方法。 可以理解爲是button 去通知OnClick有事件發生,然後OnClick就去幹活啦。
回到代碼:
Observable 就是那個button
subscrible 就是那個setOnClickListener
所以現在知道上面的代碼爲什麼沒有效果吧,因爲你沒有處理一個類似OnClick的方法,就好比你給按鈕設置了點擊事件,也設置了監聽,然後你的OnClick沒寫肯定什麼事情都不會發生了。
然後上面寫了subscrible ()需要傳一個Observer對象,就理解爲讓你去實現OnClick事件的具體操作吧。
所以代碼修改如下:
這段代碼,可以看成Observable 突突突發射了"one", "two", "three", "four", "five" 這5個字符串,然後發射到了onNext方法裏面。onError是發送失敗的情況下會回調,onComplete是所有都發射完成之後會回調。
增加調試日誌之後再來看:
運行結果:可以看出當Observable 通過subscrible 訂閱了Observer之後,就調用了onSubscribe(別問我怎麼知道的,因爲這個單詞TM就是訂閱的意思!!!)然後就開始突突突調用onNext方法,把just裏面的每一個值都給推過來。然後執行結束了,就調用了onComplete說明完成了。
再補充一下這個onSubscribe方法的參數Disposable,我們可以輸出一下
Log.v("zyl","onSubscribe Disposable:"+d.isDisposed());
看到結果,這個值爲false,這個單詞翻譯過來是一次性的意思,isDisposed 爲flase,那就不是一次性的意思,其實這個是代表事件訂閱,
由於不是一次性,所以後面的onNext纔可以不斷去接收。
接下來,我們可以嘗試在某種情況下改爲true,看看效果,代碼如下:
Observable.create(new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> emitter) throws Exception { emitter.onNext("one"); emitter.onNext("two"); emitter.onNext("three"); emitter.onNext("four"); emitter.onComplete(); //結束 } }).subscribe(new Observer<String>() { Disposable disposable; @Override public void onSubscribe(Disposable d) { disposable = d; } @Override public void onNext(String s) { if("three".equals(s)){ disposable.dispose(); } Log.v("zyl", "onNext:" + s); } @Override public void onError(Throwable e) { Log.v("zyl", "onError"); } @Override public void onComplete() { Log.v("zyl", "onComplete"); } });
輸出結果:可以看到,當爲three之後,我們調用dispose取消訂閱,則後續事件不再接收了。
值得注意的是,訂閱了事件後沒有及時取訂,容易造成內存溢出。這個後續再講
好了,到這裏Rx基本算入門了,其實just只是其中一個操作符,只是爲了方便我們調用的一種寫法,誰也不會沒事只突突突發幾個字符串吧。下面來看一下標準的寫法:
Observable.create(new ObservableOnSubscribe<Object>() { @Override public void subscribe(ObservableEmitter<Object> emitter) throws Exception { emitter.onNext("one"); emitter.onNext("two"); emitter.onNext("three"); emitter.onNext("four"); emitter.onComplete(); //結束 emitter.onNext("five"); } }).subscribe(new Observer<Object>() { @Override public void onSubscribe(Disposable d) { Log.v("zyl","onSubscribe Disposable:"+d.isDisposed()); } @Override public void onNext(Object o) { Log.v("zyl","onNext:"+o); } @Override public void onError(Throwable e) { Log.v("zyl","onError:"); } @Override public void onComplete() { Log.v("zyl","onComplete"); } }); }
這段代碼其實很好理解:通過Observable.create 創建了發射器,分別發射了6個事件。然後下面就會依次接受事件。可以看一下輸出:
可以看到,其實只接收了5個事件,"five"沒有發送過來,是因爲一旦發射了onComplete事件,下游就會停止接收了。
當然了,下游接收的時候直接new Observer()的需要重寫幾個方法,比較累贅,所以他提供了一種更簡單的方法:可以用 Consumer 對象來代替 Observer 對象
代碼如下:
Observable.create(new ObservableOnSubscribe<Object>() { @Override public void subscribe(ObservableEmitter<Object> emitter) throws Exception { emitter.onNext("one"); emitter.onNext("two"); emitter.onNext("three"); emitter.onNext("four"); emitter.onComplete(); //結束 emitter.onNext("five"); } }).subscribe(new Consumer<Object>() { @Override public void accept(Object o) throws Exception { Log.v("zyl","accept:"+o); } }); }
Consumer很好理解,就是消費者的意思,其實就是相當於只重寫了之前的onNext方法,只是這裏改爲accept,可以看一下輸出結果:
Ok,截止到目前,再來回顧一下:
1.我們先是用了just操作符,看到了很簡單的發射數據方式
2.我們認識了通過create的標準寫法方式
3.通過Consumer去替代Observer,這種寫法雖然更簡潔,但就沒有Observer那麼強大。
接下來,爲了體會到他更強大的地方,我們必須多認識幾個操作符,因爲光知道一個just還不夠強大,多認識幾個之後,以後實戰纔不會伸展不出手腳。
接下來,繼續學習除just之外的操作符:map
先來看看源碼對map的解釋:
/** * Returns an Observable that applies a specified function to each item emitted by the source ObservableSource and * emits the results of these function applications. * <p> * <img width="640" height="305" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/map.png" alt=""> * <dl> * <dt><b>Scheduler:</b></dt> * <dd>{@code map} does not operate by default on a particular {@link Scheduler}.</dd> * </dl> * * @param <R> the output type * @param mapper * a function to apply to each item emitted by the ObservableSource * @return an Observable that emits the items from the source ObservableSource, transformed by the specified * function * @see <a href="http://reactivex.io/documentation/operators/map.html">ReactiveX operators documentation: Map</a> */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) { ObjectHelper.requireNonNull(mapper, "mapper is null"); return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper)); }
乍一看好像很複雜,不用慌。我把要點提取出來:
上面的意思就是說調用map的時候,需要傳一個Function對象進來,這個Function對象有兩個參數類型,一個是T,一個是R。
R:代表的是輸出類型,上面好像沒有解釋T,實際T是輸入類型。合起來的意思就是當發射器每次調用onNext方法發射時候,會先經過map指定的這個Function,同時你要把輸入類型是什麼,輸出類型是什麼告訴他。下面就來寫個簡單的例子:
比如我現在onNext方法每次發射一個數字,然後要把在這個數字後面加個人民幣符號,然後當成字符串輸出,那麼可以這樣做。
Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> emitter) throws Exception { emitter.onNext(1); emitter.onNext(2); emitter.onNext(3); emitter.onNext(4); emitter.onComplete(); //結束 emitter.onNext(5); } }).map(new Function<Integer, String>() { @Override public String apply(Integer integer) throws Exception { return integer+"¥"; } }).subscribe(new Consumer<String>() { @Override public void accept(String str) throws Exception { Log.v("zyl","accept:"+str); } }); }
來看下輸出結果:
看到這裏,你應該聯想一下平時項目中的一些應用場景是不是可以通過它來實現?
接下來介紹一個更強大的操作符,Zip,在實際應用場景中,我們往往會從兩個地方獲取數據,再合併到一個地方輸出。比如從A接口獲取數據1234,從B接口獲取字符串ABCDE,然後在合併到一個地方組合輸出,就用到zip。
先來看下方法調用:
@SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) public static <T1, T2, R> Observable<R> zip( ObservableSource<? extends T1> source1, ObservableSource<? extends T2> source2, BiFunction<? super T1, ? super T2, ? extends R> zipper) { ObjectHelper.requireNonNull(source1, "source1 is null"); ObjectHelper.requireNonNull(source2, "source2 is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2); }
上面的代碼總結下來就是zip我需要傳三個參數,前面兩個都是Observable,最後一個是Bifunction。前面兩個就是剛說的一個用來發射12345的,第二個就是用來發射ABC的,然後通過最後一個方法Bifunction將數據組合到一起。這個Bifunction有三個參數,第一個是12345的類型,那這裏就是 Integer,第二個是ABC的類型,那就是String,最後一個是組合輸出後的類型,那就是String了。當然 ,如果是在現實場景,往往是兩個對象數據,組合成一個特殊對象數據,這裏爲了簡單演示,就用數字跟字符串了。
看看代碼怎麼寫,先寫兩個Observable,跟以前的寫法差不多,我這裏封裝到兩個方法裏面,方便調用:
private Observable<Integer> getIntegerObservable() { return Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> emitter) throws Exception { emitter.onNext(1); Log.v("zyl","emitter:1"); emitter.onNext(2); Log.v("zyl","emitter:2"); emitter.onNext(3); Log.v("zyl","emitter:3"); emitter.onNext(4); Log.v("zyl","emitter:4"); emitter.onNext(5); Log.v("zyl","emitter:5"); } }); }
private Observable<String> getStringObservable() { return Observable.create(new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> emitter) throws Exception { emitter.onNext("A"); Log.v("zyl","emitter:A"); emitter.onNext("B"); Log.v("zyl","emitter:B"); emitter.onNext("C"); Log.v("zyl","emitter:C"); } }); }
這兩個事件源準備好了,接下里就用zip組合了:
Observable.zip(getIntegerObservable(),getStringObservable(), new BiFunction<Integer,String, String>() { @Override public String apply( Integer integer,String s) throws Exception { return s + integer; } }).subscribe(new Consumer<String>() { @Override public void accept(String s) throws Exception { Log.v("zyl","accept:"+s); } });
代碼非常簡單。將前面兩個方法作爲參數傳進去就好了。來看看輸出結果:
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: emitter:1
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: emitter:2
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: emitter:3
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: emitter:4
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: emitter:5
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: accept:A1
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: emitter:A
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: accept:B2
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: emitter:B
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: accept:C3
05-25 10:12:49.172 21434-21434/cm.jkinvest.com.rxjavaproject V/zyl: emitter:C
看到這數據,可能會有點蒙圈,感覺是事件12345一塊輸出完再輸出組合數據,再輸出事件ABC,感覺毫無規律,實際上這是因爲在同個線程的原因。但是我們在實際應用中,其實只關心我們的zip合併後的數據,這裏應該篩選看,比如只看zip接收:
可以看到確實達到我們想要的效果 組合A1、B2、C3
看到這裏,可能會想45去哪了,其實這是zip的一個特點,他其實是分別從兩個發射數據源的Observable取數據,當發現取ABC之後,已經沒有了,那他也就不去另外的Observable取了。所以只有兩兩配對的組合結果。
再總結看看上面的數據,其實12345全部發射了、ABC也全部發射了、但zip組合只接收了A1、B2、C3。