RxJava使用總結
前言
RxJava出來已經很久了, 但我至今對它仍然不是很熟悉, 今天就係統的總結一下RxJava的常見用法和操作符.
首先開啟RxJava在github的主頁
https://github.com/ReactiveX/RxJava
點進去檢視介紹, 可以看到都是英文的.說點題外話, 以前我也是比較害怕英文文件的, 但是最近有點變化, 大概閱讀原始碼的時候發現把原始碼上的英文註釋看懂之後再去看原始碼遠遠比直接看原始碼去理解容易得多.英文一遍讀不懂可以多讀幾遍, 就當做閱讀理解一樣, 只要備好工具(網易有道詞典什麼的), 理解句子中所有單詞的意思, 最終理解出來的意思一般和原本表達的意思差不到哪裡去的.如果像大段的英文文件, 就先用google翻譯把整個頁面翻譯一下, 通讀一遍找到你最感興趣的資訊(因為一般我們都是在一大段文章中找一個點而已), 找到之後恢復成英文頁面再去閱讀那個點.
比如我想找到rxjava最新的版本號, 我就找到了這裡
文件說關於maven, gradle等依賴資訊可以在超連結中找到.然後以我就點選那個超連結
可以看到RxJava1最新的版本號是1.3.4(本篇只討論RxJava1)
以下是我的參考資料:
給 Android 開發者的 RxJava 詳解
可能是東半球最全的RxJava使用場景小結
一些基礎操作
關於rxjava的觀察者模式, 舉個例子, 就像電燈和開關.檯燈作為觀察者, 始終觀察著開關的一舉一動, 而開關就作為被觀察者.開關一旦開啟, 檯燈就亮起;開關一旦關閉, 檯燈就熄滅.
建立被觀察者Observable
基於上面的例子, 我們把Observable(被觀察者)取名為switcher(開關)
// 開關作為被觀察者
Observable<String> switcher = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("on");
subscriber.onNext("off");
subscriber.onNext("on");
subscriber.onNext("on");
subscriber.onCompleted();
}
});
對於建立Observable還有兩種更"偷懶"的方式
// 建立被觀察者的偷懶模式1
Observable<String> switcher = Observable.just("on", "off", "on", "on");
// 建立被觀察者的偷懶模式2
String[] k = {"on", "off", "on", "on"};
Observable<String> switcher2 = Observable.from(k);
建立觀察者Observer
我們把Observer(觀察者)取名為light(電燈)
// 電燈作為觀察者, 對始終在觀察者開關的動作, 對開關的動作而做出相應的反應
Subscriber<String> light = new Subscriber<String>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: ");
}
@Override
public void onNext(String s) {
Log.d(TAG, "onNext: " + s);
}
};
然後是被觀察者訂閱了觀察者(這個邏輯和我們通常認為的不符, 但是方便了API的設計), 被觀察者會持有觀察者的引用
switcher.subscribe(light);
錯誤處理
Observable<Integer> observable = Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
subscriber.onNext(1);
subscriber.onNext(2);
subscriber.onNext(0);
subscriber.onNext(3);
subscriber.onNext(5/0);
subscriber.onCompleted();
}
});
Subscriber<Integer> subscriber = new Subscriber<Integer>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
// subscriber的onNext中出現的異常和observable和call方法中的給onNext()傳引數出現的異常都會出現在這裡
// 總之就是call()方法中發生的異常都會出現在這裡
Log.e(TAG, "onError: " + e.getMessage());
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: " + 10 / integer);
}
};
observable.subscribe(subscriber);
過濾(filter)
Observable.just("on", "off", "on", "on")
.filter(new Func1<String, Boolean>() {
@Override
public Boolean call(String s) {
// 如果這裡返回了true資料就會被回撥到onNext, 否則返回了false就會被過濾掉
return TextUtils.equals("on", s);
}
})
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: ");
}
@Override
public void onNext(String s) {
Log.d(TAG, "onNext: " + s);
}
});
scheduler(執行緒切換)
Observable.create(new Observable.OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Log.d(TAG, "call: thread = " + Thread.currentThread().getName());
Drawable drawable = getResources().getDrawable(R.mipmap.avatar);
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io()) // 指定 subscribe()發生在IO執行緒, 即在訂閱這個過程發生在IO執行緒
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber的回撥發生在主執行緒
.subscribe(new Observer<Drawable>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Drawable drawable) {
Log.d(TAG, "onNext: thread = " + Thread.currentThread().getName());
mIv.setImageDrawable(drawable);
}
});
這裡需要說明一下了, subscribeOn()
指定 subscribe()
所發生的執行緒,即 Observable.OnSubscribe
被啟用時所處的執行緒。或者叫做事件產生的執行緒(資料發出的執行緒)。 observeOn()
: 指定 Subscriber
所執行在的執行緒。或者叫做事件消費的執行緒。Subscriber
並不是(嚴格說應該為『不一定是』,但這裡不妨理解為『不是』)subscribe()
引數中的Subscriber
,而是 observeOn()
執行時的當前 Observable
所對應的 Subscriber
,即它的直接下級 Subscriber
。換句話說,observeOn()
指定的是它之後的操作所在的執行緒。因此如果有多次切換執行緒的需求,只要在每個想要切換執行緒的位置呼叫一次 observeOn()
即可
Observable.just(1, 2, 3, 4) // IO 執行緒,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.map(mapOperator) // 新執行緒,由 observeOn() 指定
.observeOn(Schedulers.io())
.map(mapOperator2) // IO 執行緒,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread)
.subscribe(subscriber); // Android 主執行緒,由 observeOn() 指定
不同於 observeOn(),subscribeOn() 的位置放在哪裡都可以,但它是隻能呼叫一次的。
圖中共有5處含有對事件的操作。由圖中可以看出,①和②兩處受第一個 subscribeOn()
影響,執行在紅色執行緒;③和④處受第一個observeOn()
的影響,執行在綠色執行緒;⑤處受第二個observeOn()
影響,執行在紫色執行緒;而第二個subscribeOn()
,由於在通知過程中執行緒就被第一個 subscribeOn()
截斷,因此對整個流程並沒有任何影響。這裡也就回答了前面的問題:當使用了多個subscribeOn()
的時候,只有第一個 subscribeOn()
起作用。
doOnSubscribe()
預設情況下,doOnSubscribe()
執行在subscribe()
發生的執行緒(其實這一點我持懷疑態度);而如果在doOnSubscribe()
之後有subscribeOn()
的話,它將執行在離它最近的 subscribeOn()
所指定的執行緒。
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
Log.d(TAG, "call: thread = " + Thread.currentThread().getName());
subscriber.onNext(1);
subscriber.onNext(2);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io()) // 指定事件發生在io執行緒
.doOnSubscribe(new Action0() { // doOnSubscribe在subscribe()呼叫後而且在事件傳送前執行
@Override
public void call() {
Log.d(TAG, "call: thread = " + Thread.currentThread().getName());
Log.d(TAG, "call: 資料傳送之前顯示progressbar");
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定doOnSubscribe()發生在主執行緒
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.d(TAG, "call: thread = " + Thread.currentThread().getName());
Log.d(TAG, "call: 輸出最終的資料" + integer);
}
});
map(變換)
Observable.just(R.mipmap.avatar)
.map(new Func1<Integer, Drawable>() {
@Override
public Drawable call(Integer integer) {
Drawable drawable = getResources().getDrawable(integer);
return drawable;
}
})
.subscribe(new Subscriber<Drawable>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Drawable drawable) {
mIv.setImageDrawable(drawable);
}
});
compose(對Observable整體的變換)
和lift()
的區別在於,lift()
是針對事件項和事件序列的,而compose()
是針對Observable
自身進行變換。compose()
方法其實挺有用的.
Observable.Transformer<Integer, Drawable> transformer = new Observable.Transformer<Integer, Drawable>() {
@Override
public Observable<Drawable> call(Observable<Integer> observable) {
return observable.map(new Func1<Integer, Drawable>() {
@Override
public Drawable call(Integer integer) {
Drawable drawable = getResources().getDrawable(integer);
return drawable;
}
}).map(new Func1<Drawable, Drawable>() {
@Override
public Drawable call(Drawable drawable) {
return drawable;
}
}); // observable.map後面可以繼續.lift
}
};
Observable.just(R.mipmap.avatar)
// compose是為了將一系列的變換方法封裝起來
.compose(transformer)
.subscribe(new Action1<Drawable>() {
@Override
public void call(Drawable drawable) {
mIv.setImageDrawable(drawable);
}
});
flatmap
Student student1 = new Student();
student1.setStudentName("小明");
List<Student.Course> courseList1 = new ArrayList<>();
courseList1.add(new Student.Course("語文"));
courseList1.add(new Student.Course("數學"));
student1.setCourses(courseList1);
Student student2 = new Student();
student2.setStudentName("小紅");
List<Student.Course> courseList2 = new ArrayList<>();
courseList2.add(new Student.Course("英語"));
courseList2.add(new Student.Course("化學"));
student2.setCourses(courseList2);
Student[] students = {student1, student2};
Observable.from(students)
.flatMap(new Func1<Student, Observable<Student.Course>>() {
@Override
public Observable<Student.Course> call(Student student) {
return Observable.from(student.getCourses());
}
})
.subscribe(new Subscriber<Student.Course>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Student.Course course) {
Log.d(TAG, "onNext: course = " + course.courseName);
}
});
doOnNext
Observable.just(1, 2, 3)
.doOnNext(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.d(TAG, "doOnNext: integer = " + integer);
}
})
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.d(TAG, "call: 最終輸出 " + integer);
}
});
一些進階的操作
timer(定時操作)
Observable.timer(3, TimeUnit.SECONDS)
.subscribe(new Subscriber<Long>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: ");
}
@Override
public void onNext(Long aLong) {
Log.d(TAG, "onNext: " + aLong);
}
});
interval(週期性操作)
Observable.interval(2, TimeUnit.SECONDS)
.subscribe(new Subscriber<Long>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: ");
}
@Override
public void onNext(Long aLong) {
Log.d(TAG, "onNext: " + aLong);
}
});
throttleFirst(在每次事件觸發後的一定時間間隔內丟棄新的事件)
注: 這裡引入了rxbinding
// 只返回一秒內的第一個, 後續發射出來的全部丟棄
RxView.clicks(button)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(new Observer<Object>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: ");
}
@Override
public void onNext(Object o) {
Log.d(TAG, "onNext: button clicked");
}
});
schedulePeriodically(輪詢請求)
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(final Subscriber<? super String> observer) {
Schedulers.newThread()
.createWorker()
.schedulePeriodically(new Action0() {
@Override
public void call() {
observer.onNext("呵呵");
}
}, 0, 3, TimeUnit.SECONDS);
}
}).subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.d(TAG, "call: " + s);
}
});
concat(將若干個Observable串聯起來)
Observable.concat(Observable.just(1, 2, 3), Observable.just(4, 5, 6))
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.d(TAG, "concat: " + integer);
}
});
zip(將若干個Observable發射的資料組合)
Observable.zip(Observable.just(1, 2, 3), Observable.just("A", "B", "C"), new Func2<Integer, String, String>() {
@Override
public String call(Integer integer, String s) {
return integer + s;
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.d(TAG, "zip: " + s);
}
});
merge
// 拼接兩個Observable的輸出,不保證順序,按照事件產生的順序傳送給訂閱者
// 與concat的區別在於不保證順序, 按照事件產生的順序
Observable.merge(Observable.just("1", "2", "3"), Observable.just("A", "B", "C"))
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.d(TAG, "merge: " + s);
}
});
combineLatest
注: 可以用來處理表單驗證
mFirstObservable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(final Subscriber<? super String> subscriber) {
mEtFirst.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
subscriber.onNext(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
});
mSecondObservable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(final Subscriber<? super String> subscriber) {
mEtSecond.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
subscriber.onNext(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
});
Observable<Data> combineLatest = Observable.combineLatest(mFirstObservable, mSecondObservable, new Func2<String, String, Data>() {
@Override
public Data call(String s, String s2) {
return new Data(s, s2);
}
});
combineLatest.subscribe(new Action1<Data>() {
@Override
public void call(Data data) {
mTv.setText("first = " + data.first +", seoncd = " + data.second);
}
});
上面的例子可以用rxbinding簡化
Observable<CharSequence> ObservableEmail = RxTextView.textChanges(mEmailView);
Observable<CharSequence> ObservablePassword = RxTextView.textChanges(mPasswordView);
Observable.combineLatest(ObservableEmail, ObservablePassword, new Func2<CharSequence, CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence email, CharSequence password) {
return isEmailValid(email.toString()) && isPasswordValid(password.toString());
}
}).subscribe(new Subscriber<Boolean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Boolean verify) {
if (verify) {
mEmailSignInButton.setEnabled(true);
} else {
mEmailSignInButton.setEnabled(false);
}
}
});
combineLatest與zip的區別在於, zip發射的資料是一一對應的, 如果一個一直髮射資料而另一個不發射資料, 下游是獲取不到資料的.而combineLates則是: 只要有一個資料來源發射了資料, 就會呼叫call方法
與retrofit結合
public interface GankAPI {
// http://gank.io/api/data/福利/10/1
@GET("福利/{pagesize}/{page}")
Observable<GankMeiziResult> getMeiziData(@Path("pagesize") int pageSize, @Path("page") int page);
}
private RetrofitManager() {
mRetrofit = new Retrofit.Builder()
.client(genericClient())
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
// 所有的請求方法都放在同一個interface裡, 所以這裡建立出來的class不會很多, 所以可以用這一個
mGankAPI = mRetrofit.create(GankAPI.class);
}
public GankAPI getGankAPI() {
return mGankAPI;
}
GankAPI gankAPI = APIFactory.createGankAPI();
// 這裡的gankAPI.getMeiziData(10, 1)代替之前的call
gankAPI.getMeiziData(10, 1)
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Action0() {
@Override
public void call() {
showProgressDialog();
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定doOnSubscribe()所發生的執行緒, 其實這句程式碼可以不加的
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GankMeiziResult>() {
@Override
public void onCompleted() {
hideProgressDialog();
}
@Override
public void onError(Throwable e) {
hideProgressDialog();
}
@Override
public void onNext(GankMeiziResult gankMeizhiResult) {
mMeiZiAdapter = new MeiZiAdapter(gankMeizhiResult.beauties);
mRv.setAdapter(mMeiZiAdapter);
}
});
使用compose簡化執行緒指定
gankAPI.getMeiziData(10, 1)
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Action0() {
@Override
public void call() {
// 貌似doOnSubsribe的執行緒不受前面subscribeOn()指定執行緒的影響, 預設為主執行緒
Log.d(TAG, "currentThread = " + Thread.currentThread().getName());
showProgressDialog();
}
})
.compose(new Observable.Transformer<GankMeiziResult, GankMeiziResult>() {
@Override
public Observable<GankMeiziResult> call(Observable<GankMeiziResult> observable) {
return observable.observeOn(AndroidSchedulers.mainThread());
}
})
.subscribe(new Subscriber<GankMeiziResult>() {
@Override
public void onCompleted() {
hideProgressDialog();
}
@Override
public void onError(Throwable e) {
hideProgressDialog();
}
@Override
public void onNext(GankMeiziResult gankMeizhiResult) {
mMeiZiAdapter = new MeiZiAdapter(gankMeizhiResult.beauties);
mRv.setAdapter(mMeiZiAdapter);
}
});
進一步簡化
public class RxSchedulersHelper {
public static <T> Observable.Transformer<T, T> io2main() {
return new Observable.Transformer<T, T>() {
@Override
public Observable<T> call(Observable<T> observable) {
return observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
gankAPI.getMeiziData(10, 1)
.compose(RxSchedulersHelper.<GankMeiziResult>io2main())
.doOnSubscribe(new Action0() {
@Override
public void call() {
// 貌似doOnSubsribe的執行緒不受前面subscribeOn()指定執行緒的影響, 預設為主執行緒
Log.d(TAG, "currentThread = " + Thread.currentThread().getName());
showProgressDialog();
}
})
.subscribe(new Subscriber<GankMeiziResult>() {
@Override
public void onCompleted() {
hideProgressDialog();
}
@Override
public void onError(Throwable e) {
hideProgressDialog();
}
@Override
public void onNext(GankMeiziResult gankMeizhiResult) {
mMeiZiAdapter = new MeiZiAdapter(gankMeizhiResult.beauties);
mRv.setAdapter(mMeiZiAdapter);
}
});
其實這裡一些filter, map等統一操作都可以放在這個helper的compose裡面
retrywhen()錯誤重連機制
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
subscriber.onNext(1);
subscriber.onNext(2);
subscriber.onNext(3 / mDivisor);
subscriber.onCompleted();
}
})
.retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
@Override
public Observable<?> call(final Observable<? extends Throwable> observable) {
return observable.flatMap(new Func1<Throwable, Observable<?>>() {
@Override
public Observable<?> call(Throwable throwable) {
if (throwable instanceof ArithmeticException) {
if (++retryCount <= maxRetries) {
Log.d(TAG, "正在重試");
if (retryCount == 3) {
mDivisor = 1;
}
return Observable.timer(2, TimeUnit.SECONDS);
}
}
return Observable.error(throwable);
}
});
}
})
.subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: ");
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: integer = " + integer);
}
});
github地址
https://github.com/mundane799699/AndroidProjects/tree/master/MyRxJavaSummary
總結的有點累, 寫的有點亂, 畢竟不是太熟, 個人水平也有限.如有疏漏, 請幫助我指出, 感謝您的閱讀.
相關文章
- Rxjava工作原理總結RxJava
- RxJava2 操作符總結RxJava
- RxJava 系列-1:一篇的比較全面的 RxJava2 方法總結RxJava
- RxJava2 系列-1:一篇的比較全面的 RxJava2 方法總結RxJava
- templatejs使用總結JS
- VideoJs使用總結IDEJS
- VUE 使用總結Vue
- Audio使用總結
- HelloCharts 使用總結
- Git 使用總結Git
- ProgressDialog使用總結
- swagger使用總結Swagger
- npm使用總結NPM
- Supervisor 使用總結
- IDEA使用總結Idea
- SVN使用總結
- kvm使用總結
- jmeter 使用總結JMeter
- Vuex使用總結Vue
- Gson使用總結
- Toolbar使用總結
- Ajax使用總結
- Nginx使用總結Nginx
- sqlserver 使用總結SQLServer
- CompletableFuture 使用總結
- Eureka使用總結
- 前端this使用總結前端
- 大話RxJava:三、RxJava的中級使用方法RxJava
- Android RxJava系列三: 與Retrofit2結合使用和封AndroidRxJava
- RxJava 系列-3:使用 SubjectRxJava
- 2章 RxJava基本使用RxJava
- RxJava基礎使用(一)RxJava
- dorado 7 使用總結
- Rust Cargo使用總結RustCargo
- iconfonts使用的總結
- ListenalbeFuture的使用總結
- Spring Cloud使用總結SpringCloud
- artisan 命令使用總結
- SSH key使用總結