前言
最近看到各個公司的項目都在用RxJava,意識到必須要學(xué)習(xí)一下這個庫了,所以找到了“扔物線”所發(fā)表的文章<a >“給 Android 開發(fā)者的 RxJava 詳解”</a>,以下是個人在學(xué)習(xí)過程中的一些筆記與摘要,在此分享蔫骂。
RxJava有四個概念
1、Observable---被觀察者
2、Observer---觀察者
3徒役、subscribe---訂閱
4、事件
RxJava中有三個主要回調(diào)
1窖壕、onNext() ----事件忧勿,多個onNext()會組成事件隊列執(zhí)行。
2瞻讽、onCompleted() ----事件隊列完結(jié)鸳吸,當(dāng)沒有新的onNext事件發(fā)出時,就會調(diào)用onCompleted()速勇。
3晌砾、onError() ----事件隊列異常,當(dāng)事件隊列執(zhí)行過程發(fā)生異常就會調(diào)用該方法烦磁,并且不會再執(zhí)行后續(xù)的onNext()事件养匈。
Observer和Subscriber
1、Subscriber是Observer的子類个初,使用方式基本完全一樣乖寒,并且Observer在使用的時候,也會被先轉(zhuǎn)換成為一個Subscriber后執(zhí)行院溺。
2楣嘁、Subscriber和Observer的區(qū)別主要有兩點:
1)onStart():Subscriber所新增的方法,在subscribe訂閱剛開始珍逸,事件發(fā)送之前被調(diào)用逐虚。但是該方法無法指定線程,它會在subscribe發(fā)生的線程被執(zhí)行谆膳。
2)unsubscribe(): 這是 Subscriber 所實現(xiàn)的另一個接口 Subscription 的方法叭爱,用于取消訂閱。在這個方法被調(diào)用后漱病,Subscriber 將不再接收事件买雾。一般在這個方法調(diào)用前把曼,可以使用 isUnsubscribed() 先判斷一下狀態(tài)。 unsubscribe() 這個方法很重要漓穿,因為在 subscribe() 之后嗤军, Observable 會持有 Subscriber 的引用,這個引用如果不能及時被釋放晃危,將有內(nèi)存泄露的風(fēng)險叙赚。所以最好保持一個原則:要在不再使用的時候盡快在合適的地方(例如 onPause() onStop() 等方法中)調(diào)用 unsubscribe() 來解除引用關(guān)系,以避免內(nèi)存泄露的發(fā)生僚饭。
基本代碼示例
private void loadImg() {
final int resId = R.mipmap.ic_launcher;
Observable.create(new Observable.OnSubscribe<Drawable>() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(resId);
subscriber.onNext(drawable);
subscriber.onCompleted();
}
}).subscribe(new Observer<Drawable>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(Drawable drawable) {
mImageView.setImageDrawable(drawable);
}
});
}
Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onCompleted() {
Log.d("MainActivity", "事件隊列結(jié)束");
}
@Override
public void onError(Throwable e) {
Log.d("MainActivity", "事件隊列出現(xiàn)異常");
}
@Override
public void onNext(String s) {
Log.d("MainActivity", s);
}
};
Observable observable = Observable.from(new String[]{"休眠開始", "休眠結(jié)束"}).just("新的吧");
observable.subscribe(subscriber);
線程控制--Scheduler
在不指定線程的情況下震叮, RxJava 遵循的是線程不變的原則,即:在哪個線程調(diào)用 subscribe()鳍鸵,就在哪個線程生產(chǎn)事件苇瓣;在哪個線程生產(chǎn)事件,就在哪個線程消費事件权纤。如果需要切換線程钓简,就需要用到 Scheduler (調(diào)度器)。
一汹想、RxJava內(nèi)置的幾個Scheduler
1、Schedulers.immediate(): 直接在當(dāng)前線程運行撤蚊,相當(dāng)于不指定線程古掏。這是默認(rèn)的 Scheduler。
2侦啸、Schedulers.newThread(): 總是啟用新線程槽唾,并在新線程執(zhí)行操作。
3光涂、Schedulers.io(): I/O 操作(讀寫文件庞萍、讀寫數(shù)據(jù)庫、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler忘闻。行為模式和 newThread() 差不多钝计,區(qū)別在于 io() 的內(nèi)部實現(xiàn)是是用一個無數(shù)量上限的線程池,可以重用空閑的線程齐佳,因此多數(shù)情況下 io() 比 newThread() 更有效率私恬。不要把計算工作放在 io() 中,可以避免創(chuàng)建不必要的線程炼吴。
4本鸣、Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算硅蹦,即不會被 I/O 等操作限制性能的操作荣德,例如圖形的計算闷煤。這個 Scheduler 使用的固定的線程池,大小為 CPU 核數(shù)涮瞻。不要把 I/O 操作放在 computation() 中曹傀,否則 I/O 操作的等待時間會浪費 CPU。
5饲宛、另外皆愉, Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行艇抠。
二幕庐、使用方式。
有了這幾個 Scheduler 家淤,就可以使用 subscribeOn() 和 observeOn() 兩個方法來對線程進行控制了异剥。 * subscribeOn(): 指定 subscribe() 所發(fā)生的線程,即 Observable.OnSubscribe 被激活時所處的線程絮重≡┦伲或者叫做事件產(chǎn)生的線程。 * observeOn(): 指定 Subscriber 所運行在的線程青伤《搅或者叫做事件消費的線程。
示例代碼如下:
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io()) // 指定 subscribe() 發(fā)生在 IO 線程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回調(diào)發(fā)生在主線程
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer number) {
Log.d(tag, "number:" + number);
}
});
變換
1狠角、map():一對一的變換
示例代碼:
Observable.just("images/logo.png") // 輸入類型 String
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String filePath) { // 參數(shù)類型 String
return getBitmapFromPath(filePath); // 返回類型 Bitmap
}
})
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) { // 參數(shù)類型 Bitmap
showBitmap(bitmap);
}
});
上面示例代碼可以看到号杠,變換的作用就是在結(jié)果被回調(diào)之前,對輸入的數(shù)據(jù)進行處理丰歌,再輸出到observer姨蟋。
上述代碼我們看到一個Func1,F(xiàn)unc和Action其實很相似立帖,只是Func是帶有返回值的眼溶,同樣的Func也有多個FuncX。
map()所實現(xiàn)的變換只針對一對一的情況晓勇,即一個輸入得到一個結(jié)果堂飞。
2、flatMap():一對多變換
示例代碼:
Student[] students = ...;
Subscriber<Course> subscriber = new Subscriber<Course>() {
@Override
public void onNext(Course course) {
Log.d(tag, course.getName());
}
...
};
Observable.from(students)
.flatMap(new Func1<Student, Observable<Course>>() {
@Override
public Observable<Course> call(Student student) {
return Observable.from(student.getCourses());
}
})
.subscribe(subscriber);
可以看到宵蕉,上面的代碼將每一個student所包含該的多個課程全部取了出來酝静,這就是一對多的情況,這種情況就可以通過flatMap來實現(xiàn)羡玛。
flatMap所返回的是一個Observable對象别智,但這個Observable并不會直接發(fā)送到Subscriber的回調(diào)中,而是將這個observable所發(fā)送出來的事件匯集稼稿,構(gòu)成一個新的observable薄榛,再由這個新的observable統(tǒng)一的將事件交給Subscriber的回調(diào)讳窟。
doOnSubscribe()
前面提到Subscriber中有一個onStart()方法,這個方法可以做一些初始化的操作敞恋,是在訂閱開始后丽啡,事件發(fā)送前被調(diào)用的。但是它并不能指定線程硬猫,它永遠只會在subscribe的線程中被執(zhí)行补箍。那當(dāng)我們的初始化操作需要指定線程時就需要使用到doOnSubscribe()來做了。
示例代碼:
Observable.create(onSubscribe)
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Action0() {
@Override
public void call() {
progressBar.setVisibility(View.VISIBLE); // 需要在主線程執(zhí)行
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主線程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
默認(rèn)情況下啸蜜,doOnSubscribe()也將在subscribe所發(fā)生的線程中被執(zhí)行坑雅,但是如果doOnSubscribe()之后有一個subscribeOn()的話,那么它將會在它后面最近的那個subscribeOn()所指定的線程中執(zhí)行衬横。