匯入
我相信大家肯定對ReactiveX 和 RxJava 都不陌生,因為現在只要是和技術相關的網站,部落格都會隨處見到介紹ReactiveX和RxJava的文章。
ReactiveX
- ReactiveX是Reactive Extensions 的縮寫,即響應式程式設計的擴充套件。
- “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”(一個在 Java VM 上使用可觀測的序列來組成非同步的、基於事件的程式庫)。
- Rx是一種程式設計模型,目標是提供一致的程式設計介面,幫助開發者更方便的處理非同步 資料流.由微軟的架構師Erik Meijer領導的團隊開發,在2012年11月開源。他在各個常用程式語言上都有實現。如Java,C#,PHP,Swift,Scala等等.社群網站是reactivex.io。
- ReactiveX不僅僅是一個程式設計介面,它是一種程式設計思想的突破,它影響了許多其它的程式庫和框架以及程式語言。
關於響應式程式設計
- 事件匯流排(Event buses)或我們們常見的單擊事件就是一個非同步事件流,你可以觀察這個流,也可以基於這個流做一些自定義操作。響應式就是基於這種想法。你能夠建立所有事物的資料流,而不僅僅只是單擊和懸停事件資料流。 流廉價且無處不在,任何事物都可以當作一個流:變數、使用者輸入、屬性、快取、資料結構等等。比如,假設你的微博評論就是一個跟單擊事件一樣的資料流,你能夠監聽這個流,並做出響應。
- 有一堆的函式能夠建立(create)任何流,也能將任何流進行組合(combine)和過濾(filter)。 這正是“函式式”的魔力所在。一個流能作為另一個流的輸入(input),甚至多個流也可以作為其它流的輸入。你能合併(merge)兩個流。你還能通過過濾(filter)一個流得到那些你感興趣的事件。你能將一個流中的資料對映(map)到一個新的流中。
- 響應式程式設計的主要組成部分是observable, operator 和 observer
- 一般響應式程式設計的資訊流:Observable -> Operator1 -> Operator2->...->OperatorN->Observer
- Observable 是事件的生產者,Observer是事件的最終消費者。中間可以通過任意多個的Operator對事件進行處理和轉換
- 因為Observer通常在主執行緒中執行,因此設計上要求程式碼儘可能的簡單,只對事件作出相應(不對事件或者資料進行修改,所有修改事件的工作全部由operator完成)
RxJava 和 RxAndroid
- RxJava是ReactiveX在 Java 平臺的實現,你可以將它看作一個普通的Java類庫。
- RxAndroid是RxJava的一個針對Android平臺的擴充套件,主要用於 Android 開發。
- RxJava就是一個做非同步開發的框架,和Android系統提供的 Handler+Thread,AsyncTask,Context.runOnUiThread等是解決的是同樣的問題。那麼他跟系統提供的非同步程式設計方案比,有什麼好處呢。或者說他有什麼樣的優勢值得我們花時間和精力切換到RxJava呢?
總結起來可以用兩個詞來概括:非同步和簡潔
主要概念
Observable(被觀察者)
Observables 負責發出一系列的事件,這裡的事件可以是任何東西,例如網路請求的結果,複雜計算處理的結果,資料庫操作的結構,檔案操作的結果等,事件執行結束後交給Observer的回撥處理。
Observer(觀察者)
進行訂閱接受處理事件
Operator(操作符)中文文件
負責對事件進行各種變化和處理
Scheduler(排程器)
提供了各種排程器,是RxJava可以方便的實現非同步開發
事件
這裡的事件值指的是 onNext (有新資料),onComplete (所有資料處理完成),onError (事件佇列異常)
RxJava的好處(為什麼RxJava對於Android如此重要)
- 輕鬆使用併發:讓非同步程式設計變得簡單簡潔.像寫同步程式碼一樣。
- 方便的執行緒切換
- 簡單而完善的異常處理機制:傳統的try/cache沒辦法處理非同步中子執行緒產生的異常,RxJava 提供了合適的錯誤處理機制
- 強大的操作符支援,函式式的風格,鏈式呼叫。
舉個例子
假如現在我們有這樣一個需求:介面上有一個自定義的檢視 imageCollectorView ,它的作用是顯示多張圖片,並能使用 addImage(Bitmap) 方法來任意增加顯示的圖片。現在需要程式將一個給出的目錄陣列 File[] folders 中每個目錄下的 png 圖片都載入出來並顯示在 imageCollectorView 中。需要注意的是,由於讀取圖片的這一過程較為耗時,需要放在後臺執行,而圖片的顯示則必須在 UI 執行緒執行。
目錄陣列
File[] folders=new File[]{......};複製程式碼
執行緒方式實現(call hell)
new Thread() {
@Override
public void run() {
super.run();
try{
for (File folder : folders) {
File[] files = folder.listFiles();
for (File file : files) {
if (file.getName().endsWith(".png")) {
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}catch(Exception e){
//error handling
//只能在這裡進行異常處理
}
}
}.start();複製程式碼
RxJava 實現
//建立Observable
Observable observable = Observable.create(new ObservableOnSubscribe<File>() {
@Override
public void subscribe(ObservableEmitter<File> e) throws Exception {
for (File file : files) {
e.onNext(file);
}
e.onComplete();
}
});
//建立Observer
Observer<Bitmap> observer = new Observer<Bitmap>() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
@Override
public void onError(Throwable throwable) {
//error handling
}
@Override
public void onComplete() {
Log.i(TAG,"All images are shown");
}
};
//對事件集進行處理並連線消費者
observable.flatMap(new Func1<File, Observable<File>>() {//分別獲取每個資料夾下面的檔案,組合成一個Observable
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {//過濾出所有副檔名為png的檔案
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {//根據File物件,獲取Bitmap物件
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())//指定Observable的所有操作符的操作在io執行緒中執行
.observeOn(AndroidSchedulers.mainThread())//指定消費者在主執行緒中執行
.subscribe(observer);//連線觀察者複製程式碼
有的人可能說了,你這不是程式碼更多,更復雜了嗎?
不要著急,這只是最基礎的版本,稍後會對程式碼進行簡化。
但即使是這種情況下,程式碼雖然多了,但我們可以發現,他的邏輯更清晰了,也沒有那麼多的巢狀了。
簡化程式碼
- 對於一個陣列,可用建立操作符“from”來建立Observable
- 如果我們只對結果感興趣,不關心異常處理和事件發射完成事件,我也可以將Observer用Consumer來替換
//建立Observable
Observable observable = Observable.from(folers);
//建立Observer
Consumer<Bitmap> consumer=new Consumer<Bitmap>() {
@Override
public void accept(@NonNull Bitmap bitmap) throws Exception {
imageCollectorView.addImage(bitmap);
}
};
//對事件集進行處理並連線消費者
observable.flatMap(new Func1<File, Observable<File>>() {//分別獲取每個資料夾下面的檔案,組合成一個Observable
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {//過濾出所有副檔名為png的檔案
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {//根據File物件,獲取Bitmap物件
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())//指定Observable的所有操作符的操作在io執行緒中執行
.observeOn(AndroidSchedulers.mainThread())//指定消費者在主執行緒中執行
.subscribe(consumer);//連線消費者複製程式碼
RxJava 鏈式呼叫實現
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Bitmap>() {
@Override
public void accept(@NonNull Bitmap bitmap) throws Exception {
imageCollectorView.addImage(bitmap);
}
});複製程式碼
RxJava + lambda 實現
Observable.from(folders)
.flatMap(file -> Observable.from(file.listFiles())
.filter(file -> file.getName().endsWith(".png"))
.map( file -> getBitmapFromFile(file))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> imageCollectorView.addImage(bitmap));//無異常處理,有異常會拋到主執行緒,不影響我們原來程式的crash處理複製程式碼
關於lambda(匿名函式,它可以包含表示式和語句)在Android中的使用:
- 要在 Android 的較早版本中測試 Lambda 表示式、方法引用和型別註解,請前往您的 build.gradle 檔案,將 compileSdkVersion 和 targetSdkVersion 設定為 23 或更低。您仍需要啟用 Jack 工具鏈以使用這些 Java 8 功能。
- 經測試,按照官方提供的方案配置後,雖然可以使用lambda,但編譯速度變的很慢。
- 在執行的時候,並且我測試用的專案引用改了 Bouncy Castle(輕量級加密解密工具包) 這個包報出了記憶體溢位的異常,所以我感覺現在還不太穩定。
- 第三方開源的實現方案:retrolambda
- 當然我們也可以不用lambda,這樣程式碼看著比較多,但因其只有一層巢狀的鏈式呼叫,所以邏輯結構並不複雜。事實上 Android Studio 會自動幫我們把這部分程式碼摺疊成lambda的形式。
更進一步,假設我們現在需要忽略掉前5張,一共顯示10張
Observable.from(folders)
.flatMap(file -> Observable.from(file.listFiles())
.filter(file -> file.getName().endsWith(".png"))
.skip(5)
.take(10)
.map( file -> getBitmapFromFile(file))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> imageCollectorView.addImage(bitmap));//無異常處理,有異常會拋到主執行緒,不影響我們原來程式的crash處理複製程式碼
操作符簡介
建立操作
用於建立Observable的操作符
Create
— 通過呼叫觀察者的方法從頭建立一個Observablecreate操作符是所有建立型操作符的“根”,也就是說其他建立型操作符最後都是通過create操作符來建立Observable的
From
— 將其它的物件或資料結構轉換為ObservableJust
— 將物件或者物件集合轉換為一個會發射這些物件的ObservableDefer
— 在觀察者訂閱之前不建立這個Observable,為每一個觀察者建立一個新的ObservableEmpty/Never/Throw
— 建立行為受限的特殊Observable一般用於測試
Interval
— 建立一個定時發射整數序列的ObservableRange
— 建立發射指定範圍的整數序列的ObservableRepeat
— 建立重複發射特定的資料或資料序列的ObservableStart
— 建立發射一個函式的返回值的ObservableTimer
— 建立在一個指定的延遲之後發射單個資料的Observable
變換操作
這些操作符可用於對Observable發射的資料進行變換
Map
— 對映,通過對序列的每一項都應用一個函式變換Observable發射的資料,實質是對序列中的每一項執行一個函式,函式的引數就是這個資料項Buffer
— 快取,可以簡單的理解為快取,它定期從Observable收集資料到一個集合,然後把這些資料集合打包發射,而不是一次發射一個FlatMap
— 扁平對映,將Observable發射的資料變換為Observables集合,然後將這些Observable發射的資料平坦化的放進一個單獨的Observable,可以認為是一個將巢狀的資料結構展開的過程。GroupBy
— 分組,將原來的Observable分拆為Observable集合,將原始Observable發射的資料按Key分組,每一個Observable發射一組不同的資料Scan
— 掃描,對Observable發射的每一項資料應用一個函式,然後按順序依次發射這些值Window
— 視窗,定期將來自Observable的資料分拆成一些Observable視窗,然後發射這些視窗,而不是每次發射一項。類似於Buffer,但Buffer發射的是資料,Window發射的是Observable,每一個Observable發射原始Observable的資料的一個子集
過濾操作
這些操作符用於從Observable發射的資料中進行選擇,符合一定條件的傳送給觀察者進行處理,不符合條件的直接丟棄
Filter
— 過濾,過濾掉沒有通過謂詞測試的資料項,只發射通過測試的Skip
— 跳過前面的若干項資料SkipLast
— 跳過後面的若干項資料Take
— 只保留前面的若干項資料TakeLast
— 只保留後面的若干項資料Debounce
— 只有在空閒了一段時間後才發射資料,通俗的說,就是如果一段時間沒有操作,就執行一次操作Distinct
— 去重,過濾掉重複資料項ElementAt
— 取值,取特定位置的資料項First
— 首項,只發射滿足條件的第一條資料IgnoreElements
— 忽略所有的資料,只保留/終止通知(onError或onCompleted)Last
— 末項,只發射最後一條資料Sample
— 取樣,定期發射最新的資料,等於是資料抽樣,有的實現裡叫ThrottleFirst
組合操作
組合操作符用於將多個Observable組合成一個單一的Observable
And/Then/When
— 通過模式(And條件)和計劃(Then次序)組合兩個或多個Observable發射的資料集CombineLatest
— 當兩個Observables中的任何一個發射了一個資料時,通過一個指定的函式組合每個Observable發射的最新資料(一共兩個資料),然後發射這個函式的結果Join
— 無論何時,如果一個Observable發射了一個資料項,只要在另一個Observable發射的資料項定義的時間視窗內,就將兩個Observable發射的資料合併發射Merge
— 將兩個Observable發射的資料組合併成一個StartWith
— 在發射原來的Observable的資料序列之前,先發射一個指定的資料序列或資料項Switch
— 將一個發射Observable序列的Observable轉換為這樣一個Observable:它逐個發射那些Observable最近發射的資料Zip
— 打包,使用一個指定的函式將多個Observable發射的資料組合在一起,然後將這個函式的結果作為單項資料發射
錯誤處理
這些操作符用於從錯誤通知中恢復
輔助操作
一組用於處理Observable的操作符
Delay
— 延遲一段時間發射結果資料Do
— 註冊一個動作佔用一些Observable的生命週期事件,相當於Mock某個操作Materialize/Dematerialize
— 將發射的資料和通知都當做資料發射,或者反過來ObserveOn
— 指定觀察者觀察Observable的排程程式(工作執行緒)SubscribeOn
— 指定Observable應該在哪個排程程式上執行Serialize
— 強制Observable按次序發射資料並且功能是有效的Subscribe
— 收到Observable發射的資料和通知後執行的操作TimeInterval
— 將一個Observable轉換為發射兩個資料之間所耗費時間的ObservableTimeout
— 新增超時機制,如果過了指定的一段時間沒有發射資料,就發射一個錯誤通知Timestamp
— 給Observable發射的每個資料項新增一個時間戳Using
— 建立一個只在Observable的生命週期記憶體在的一次性資源
條件和布林操作
這些操作符可用於單個或多個資料項,也可用於Observable
All
— 判斷Observable發射的所有的資料項是否都滿足某個條件Amb
— 給定多個Observable,只讓第一個發射資料的Observable發射全部資料Contains
— 判斷Observable是否會發射一個指定的資料項DefaultIfEmpty
— 發射來自原始Observable的資料,如果原始Observable沒有發射資料,就發射一個預設資料SequenceEqual
— 判斷兩個Observable是否按相同的資料序列SkipUntil
— 丟棄原始Observable發射的資料,直到第二個Observable發射了一個資料,然後發射原始Observable的剩餘資料SkipWhile
— 丟棄原始Observable發射的資料,直到一個特定的條件為假,然後發射原始Observable剩餘的資料TakeUntil
— 發射來自原始Observable的資料,直到第二個Observable發射了一個資料或一個通知TakeWhile
— 發射原始Observable的資料,直到一個特定的條件為真,然後跳過剩餘的資料
算術和聚合操作
這些操作符可用於整個資料序列
Average
— 計算Observable發射的資料序列的平均值,然後發射這個結果Concat
— 不交錯的連線多個Observable的資料Count
— 計算Observable發射的資料個數,然後發射這個結果Max
— 計算併發射資料序列的最大值Min
— 計算併發射資料序列的最小值Reduce
— 按順序對資料序列的每一個應用某個函式,然後返回這個值Sum
— 計算併發射資料序列的和
連線操作
一些有精確可控的訂閱行為的特殊Observable
Connect
— 指示一個可連線的Observable開始發射資料給訂閱者- 可連線的Observable (connectable Observable)與普通的Observable差不多,不過它並不會在被訂閱時開始發射資料,而是直到使用了Connect操作符時才會開始。用這個方法,你可以等待所有的觀察者都訂閱了Observable之後再開始發射資料。
- RxJava中connect是ConnectableObservable介面的一個方法,使用publish操作符可以將一個普通的Observable轉換為一個ConnectableObservable。
舉例
ConnectableObservable<String> connectableObservable = Observable.just("a", "c", "d").publish(); connectableObservable.subscribe(new Consumer<String>() { @Override public void accept(@NonNull String s) throws Exception { LogUtil.i(s); } }); LogUtil.i("subscribe end....."); Observable.timer(3, TimeUnit.SECONDS).subscribe(new Consumer<Long>() { @Override public void accept(@NonNull Long aLong) throws Exception { LogUtil.i("connect method called after 3 seconds."); connectableObservable.connect(); } });複製程式碼
03-20 15:54:19.328 27493-27493/me.sunbird.react_native_demo I/x_log:RxJavaActivity.testConnectableObservable(L:586): subscribe end..... 03-20 15:54:22.378 27493-27573/me.sunbird.react_native_demo I/x_log:RxJavaActivity$34.accept(L:591): connect method called after 3 seconds. 03-20 15:54:22.419 27493-27573/me.sunbird.react_native_demo I/x_log:RxJavaActivity$33.accept(L:582): a 03-20 15:54:22.419 27493-27573/me.sunbird.react_native_demo I/x_log:RxJavaActivity$33.accept(L:582): c 03-20 15:54:22.420 27493-27573/me.sunbird.react_native_demo I/x_log:RxJavaActivity$33.accept(L:582): d複製程式碼
Publish
— 將一個普通的Observable轉換為可連線的RefCount
— 使一個可連線的Observable表現得像一個普通的ObservableReplay
— 確保所有的觀察者收到同樣的資料序列,即使他們在Observable開始發射資料之後才訂閱
轉換操作
操作符決策樹
幾種主要的需求
- 直接建立一個Observable(建立操作)
- 組合多個Observable(組合操作)
- 對Observable發射的資料執行變換操作(變換操作)
- 從Observable發射的資料中取特定的值(過濾操作)
- 轉發Observable的部分值(條件/布林/過濾操作)
- 對Observable發射的資料序列求值(算術/聚合操作)
Scheduler(排程器)中文文件
本質上RxJava就是一個做非同步開發的框架,能使我們極其靈活的進行執行緒切換。
我們可以使用ObserveOn和SubscribeOn操作符,可以讓Observable在一個特定的排程器上執行,ObserveOn指示一個Observable在一個特定的排程器上呼叫觀察者的onNext, onError和onCompleted方法,SubscribeOn更進一步,它指示Observable將全部的處理過程(包括髮射資料和通知)放在特定的排程器上執行。
subscribeOn 和 observeOn 兩個操作符是極其容易混淆的,可以看下這篇部落格來徹底分清楚這兩個操作符SubscribeOn 和 ObserveOn
排程器型別 | 效果 |
---|---|
Schedulers.computation( ) | 用於計算任務,如事件迴圈或和回撥處理,不要用於IO操作(IO操作請使用Schedulers.io());預設執行緒數等於處理器的數量 |
Schedulers.from(executor) | 使用指定的Executor作為排程器 |
Schedulers.immediate( ) | 在當前執行緒立即開始執行任務 |
Schedulers.io( ) | 用於IO密集型任務,如非同步阻塞IO操作,這個排程器的執行緒池會根據需要增長;對於普通的計算任務,請使用Schedulers.computation();Schedulers.io( )預設是一個CachedThreadScheduler,很像一個有執行緒快取的新執行緒排程器 |
Schedulers.newThread( ) | 為每個任務建立一個新執行緒 |
Schedulers.trampoline( ) | 當其它排隊的任務完成後,在當前執行緒排隊開始執行 |
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
LogUtil.w("subscribe method is running in thread:" + Thread.currentThread().getName());
observableEmitter.onNext("a");
observableEmitter.onComplete();
}
}).map(new Function<String, String>() {
@Override
public String apply(@NonNull String s) throws Exception {
LogUtil.w("first map is running in thread:" + Thread.currentThread().getName());
return s;
}
}).subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
LogUtil.w("second map is running in thread:" + Thread.currentThread().getName());
return s;
}
})
.observeOn(Schedulers.io())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
LogUtil.w("third map is running in thread:" + Thread.currentThread().getName());
return s;
}
})
.observeOn(Schedulers.computation())
.map(new Function<String, String>() {
@Override
public String apply(@NonNull String s) throws Exception {
LogUtil.w("fourth map is running in thread:" + Thread.currentThread().getName());
return s;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
LogUtil.w("consumer accept method is running in thread:" + Thread.currentThread().getName());
}
});複製程式碼
執行後我們可以得到如下結果:
03-20 11:44:23.716 8687-8723/? W/x_log:RxJavaActivity$27.subscribe(L:528): subscribe method is running in thread:RxCachedThreadScheduler-1
03-20 11:44:23.717 8687-8723/? W/x_log:RxJavaActivity$28.apply(L:535): first map is running in thread:RxCachedThreadScheduler-1
03-20 11:44:23.721 8687-8724/? W/x_log:RxJavaActivity$29.apply(L:543): second map is running in thread:RxNewThreadScheduler-1
03-20 11:44:23.726 8687-8725/? W/x_log:RxJavaActivity$30.apply(L:551): third map is running in thread:RxCachedThreadScheduler-2
03-20 11:44:23.729 8687-8726/? W/x_log:RxJavaActivity$31.apply(L:559): fourth map is running in thread:RxComputationThreadPool-1
03-20 11:44:23.836 8687-8687/? W/x_log:RxJavaActivity$32.accept(L:567): consumer accept method is running in thread:main複製程式碼
RxJava 的使用場景舉例
複雜的資料變換
Observable.just("1", "2", "2", "3", "4", "5")//建立Observable
.map(Integer::parseInt)//對每一項執行Integer.parseInt方法
.filter(s -> s > 1)//過濾出所有值 >1 的物件
.distinct() //去重,這裡也可以傳遞一個方法,來定義兩個物件是否equals的策略,非常靈活
.take(3)//取到前3個
.reduce((sum, item) -> sum + item) //累加
.subscribe(System.out::println);//9 列印出最終累加的結果。複製程式碼
Retrofit結合RxJava做網路請求框架
這裡不作詳解,具體的介紹可以看扔物線的這篇文章,對RxJava的入門者有很大的啟發。其中也講到了RxJava和Retrofit如何結合來實現更簡潔的程式碼
RxJava代替EventBus進行資料傳遞
RxBus
RxBus並不是一個庫,而是一種模式,是使用了RxJava的思想來達到EventBus的資料傳遞效果。這篇文章把RxBus講的比較詳細。
square/Otto 對 RxBus 的態度
This project is deprecated in favor of RxJava and RxAndroid. These projects permit the same event-driven programming model as Otto, but they’re more capable and offer better control of threading.
為了支援 RxJava 和 RxAndroid,我們已經廢棄了這個專案。這兩個專案提供了和 Otto 一樣的基於事件驅動的程式設計模型,而且他們更強大,並提供更好的執行緒控制。
If you’re looking for guidance on migrating from Otto to Rx, this post is a good start.
如果你正在尋找從 Otto 遷移到 Rx 的教程,閱讀這篇文章將會是一個很好的開始。
一個網路請求依賴另外一個網路請求返回的結果。例如:登入之後,根據拿到的token去獲取訊息列表。
@GET("/token")
public Observable<String> getToken();
@GET("/user")
public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId);
...
getToken()
.flatMap(new Func1<String, Observable<User>>() {
@Override
public Observable<User> call(String token) {
return getUser(token, userId);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});複製程式碼
同一個頁面同事發起兩個以上的請求,此時對頁面中的ProgressBar進行管理,即兩個請求只能展示一個ProgressBar,並且所有的請求都結束後,ProgressBar 才能消失
@GET("/date1")
public Observable<String> getDate1();
@GET("/data2")
public Observable<String> getData2()
...
progressDialog.show()
Observable.merge(getData1(), getData2())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(String s) {
// 這裡進行結果處理
}
@Override
public void onError(Throwable throwable) {
//error handling
progressDialog.dismiss();
}
@Override
public void onComplete() {
progressDialog.dismiss();
}
});複製程式碼
使用throttleFirst(throttle:節流閥)防止按鈕重複點選
RxView.clicks(button)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(new Observer<Object>() {
@Override
public void onCompleted() {
log.d ("completed");
}
@Override
public void onError(Throwable e) {
log.e("error");
}
@Override
public void onNext(Object o) {
log.d("button clicked");
}
});複製程式碼
使用debounce(去抖動)做textSearch
用簡單的話講就是當N個結點發生的時間太靠近(即發生的時間差小於設定的值T),debounce就會自動過濾掉前N-1個結點。
比如在做百度地址聯想的時候,可以使用debounce減少頻繁的網路請求。避免每輸入(刪除)一個字就做一次聯想
RxTextView.textChangeEvents(inputEditText)
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<TextViewTextChangeEvent>() {
@Override
public void onCompleted() {
log.d("onComplete");
}
@Override
public void onError(Throwable e) {
log.d("Error");
}
@Override
public void onNext(TextViewTextChangeEvent onTextChangeEvent) {
log.d(format("Searching for %s", onTextChangeEvent.text().toString()));
}
});複製程式碼
RxJava 的生態
rx-preferences -使SharedPreferences支援RxJava
RxAndroid -RxJava的Android擴充
RxLifecycle -幫助使用了RxJava的安卓應用控制生命週期
RxBinding -安卓UI控制元件的RxJava繫結API
Android-ReactiveLocation -Google Play Service API wrapped in RxJava
storio -支援RxJava的資料庫
retrofit -支援RxJava的網路請求庫
sqlbrite -支援RxJava的sqlite資料庫
RxPermissions -RxJava實現的Android執行時許可權控制
reark -RxJava architecture library for Android
frodo -Android Library for Logging RxJava Observables and Subscribers.
RxJava 的現況
- RxJava 最新版 2.0.7(注:不相容1.x 的版本) 大小 2.1M
- RxAndroid 大小 10k
- 支援Java6以上,Android2.3以上
- github star 22511
RxJava 的未來展望
通用的資料流,強大的操作符,靈活的執行緒排程,簡單完善的異常處理機制,函數語言程式設計等等特性,奠定了RxJava的強大地位。
Android 系統中到處都是非同步,比如網路請求,檔案讀寫,資料庫查詢,系統服務或者第三方SDK服務等,這些非同步請求非常耗時,需要在非UI執行緒中執行,而對UI的修改又必須要在主執行緒中執行。如果再包含多個非同步執行巢狀的話,就會讓我們的程式碼顯得凌亂。通過RxJava提供的強大而通用的非同步處理機制,可以使我們的程式碼邏輯更清晰,便於後期的維護。並且現在RxJava的生態越來越大,個人認為,以後所有的涉及非同步操作的系統服務,第三方庫,第三方服務SDK都會以Observable或類Observable的方式提供給我們呼叫,而不是像現在這樣,讓我們傳遞一個又一個的listener。
參考資料
- reactivex.io
- ReactiveX文件中文翻譯
- 響應式程式設計介紹
- 給 Android 開發者的 RxJava 詳解
- RxJava 與 Retrofit 結合的最佳實踐
- Awesome-RxJava
- 在Android中使用響應式程式設計
- SubscribeOn 和 ObserveOn
分享工具
zeplin(軟體演示)
- 方便的效果圖管理
- 新檔案,修改檔案提示
- 自動測量
- 標註評論
- 支援pohtoshop和sketch
- 第一個專案免費
charles(軟體演示)
- 簡單已用,功能強大
- focus 某一個域名下的請求,方便查詢
- 對某個請求修改引數,重新請求,方便除錯
- 各種格式良好的response解析
- copy出cURL 格式的請求,方便傳遞個任何人進行請求模擬