上一篇博客介紹了Rxjava簡(jiǎn)單的使用和基本的流程,這次將會(huì)體驗(yàn)到Rxjava真正的威力。
線程控制-Scheduler(調(diào)度器)
Rxjava最大的特點(diǎn)就是異步,所以線程控制肯定是必不可少的。在Rxjava中線程控制是非常簡(jiǎn)單的。
主要的兩個(gè)方法:
subscribeOn 指定OnSubscribe的call具體實(shí)現(xiàn)的線程。
observeOn指定Subscriber的回調(diào)發(fā)生在主線程
下面對(duì)其中的Rxjava中內(nèi)置的幾個(gè)比較常見(jiàn)的Scheduler進(jìn)行簡(jiǎn)單的介紹。
- Schedulers.immediate(): 直接在當(dāng)前線程運(yùn)行,相當(dāng)于不指定線程。這是默認(rèn)的 Scheduler。
- Schedulers.newThread(): 總是啟用新線程,并在新線程執(zhí)行操作。
- Schedulers.io(): I/O 操作(讀寫(xiě)文件、讀寫(xiě)數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler。行為模式和 newThread() 差不多,區(qū)別在于 io() 的內(nèi)部實(shí)現(xiàn)是是用一個(gè)無(wú)數(shù)量上限的線程池,可以重用空閑的線程,因此多數(shù)情況下 io() 比 newThread() 更有效率。不要把計(jì)算工作放在 io() 中,可以避免創(chuàng)建不必要的線程。
- Schedulers.computation(): 計(jì)算所使用的 Scheduler。這個(gè)計(jì)算指的是 CPU 密集型計(jì)算,即不會(huì)被 I/O 等操作限制性能的操作,例如圖形的計(jì)算。這個(gè) Scheduler 使用的固定的線程池,大小為 CPU 核數(shù)。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時(shí)間會(huì)浪費(fèi) CPU。
- 另外, Android 還有一個(gè)專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運(yùn)行。
下面是一個(gè)簡(jiǎn)單的實(shí)例:模擬加載一張圖片顯示到ImageView上。
mImage = (ImageView) findViewById(R.id.image);
Observable.create(new Observable.OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getResources().getDrawable(drawableRes);
try {
Thread.sleep(7000);
//模擬圖片加載的耗時(shí)操作,如果主線程休眠7s,會(huì)觸發(fā)ANR。以此證明此處不是在主線程中執(zhí)行的。
} catch (InterruptedException e) {
e.printStackTrace();
}
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())//指定subscribe發(fā)生在IO線程
.observeOn(AndroidSchedulers.mainThread())//指定Subscriber的回調(diào)發(fā)生在主線程中
.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) {
mImage.setImageDrawable(drawable);
}
});
運(yùn)行之后,發(fā)現(xiàn)等待一段時(shí)間后,圖片就可以正常顯示在ImageView上。到這里,線程的調(diào)度的使用基本上就介紹完畢。下面開(kāi)始介紹Rxjava中的交換。
交換
這里我們先直接通過(guò)例子來(lái)說(shuō)明交換的作用和用法,然后在來(lái)介紹交換的API和具體使用方法。
考慮這樣一種需求,從數(shù)據(jù)庫(kù)中讀取所有的用戶數(shù)據(jù),用R下java實(shí)現(xiàn)如下:eg1
bservable.create(new Observable.OnSubscribe<List<User>>() {
@Override
public void call(Subscriber<? super List<User>> subscriber) {
List<User> userList = null;
···
//從數(shù)據(jù)庫(kù)獲取用戶表數(shù)據(jù)并賦給userList
···
subscriber.onNext(userList);
}
}).subscribe(new Action1<List<User>>() {
@Override
public void call(List<User> users) {
//獲取到用戶信息列表
}
});
然后,我們實(shí)際上只需要一個(gè)叫"xiaochu"的用戶,然后我們代碼就變成了下面的樣式:eg2
Observable.create(new Observable.OnSubscribe<List<User>>() {
@Override
public void call(Subscriber<? super List<User>> subscriber) {
List<User> userList = null;
···
//從數(shù)據(jù)庫(kù)獲取用戶表數(shù)據(jù)并賦給userList
···
subscriber.onNext(userList);
}
}).flatMap(new Func1<List<User>, Observable<User>>() {
@Override
public Observable<User> call(List<User> users) {
return Observable.from(users);
}
}).filter(new Func1<User, Boolean>() {
@Override
public Boolean call(User user) {
return user.getName().equals("xiaochu");
}
}).subscribe(new Action1<User>() {
@Override
public void call(User user) {
//xiaochu的數(shù)據(jù)
}
});
如果這個(gè)時(shí)候,我們需要使用xiaochu爸爸的數(shù)據(jù),那么我們可以這么實(shí)現(xiàn):eg3
Observable.create(new Observable.OnSubscribe<List<User>>() {
@Override
public void call(Subscriber<? super List<User>> subscriber) {
List<User> userList = null;
···
//從數(shù)據(jù)庫(kù)獲取用戶表數(shù)據(jù)并賦給userList
···
subscriber.onNext(userList);
}
}).flatMap(new Func1<List<User>, Observable<User>>() {
@Override
public Observable<User> call(List<User> users) {
return Observable.from(users);
}
}).filter(new Func1<User, Boolean>() {
@Override
public Boolean call(User user) {
return user.getName().equals("xiaochu");
}
}).map(new Func1<User, User>() {
@Override
public User call(User user) {
//根據(jù)xiaochu的數(shù)據(jù)user從數(shù)據(jù)庫(kù)查找出xiaochu的父親user2
return user2;
}
}).subscribe(new Action1<User>() {
@Override
public void call(User user2) {
//拿到xiaochu爸爸的數(shù)據(jù)
}
});
從上面的示例中,可以看出來(lái)Rxjava強(qiáng)大的可擴(kuò)展性吧。下面我們對(duì)其中的變化來(lái)進(jìn)行具體的分析。
** 1. map() ** 事件對(duì)象的直接變換。
map()的使用在eg3就有具體的體現(xiàn)。
在Map參數(shù)中出現(xiàn)一個(gè)叫做Func1的類,與Action1非常相似,只不過(guò)他有返回值。與Action相似的是,F(xiàn)unc也有多個(gè)(0-9)表示不同的參數(shù)的個(gè)數(shù)。
可以看到,通過(guò)map,我們將xiaochu的數(shù)據(jù)轉(zhuǎn)換呈了xiaochu爸爸的數(shù)據(jù)。
**2. flatMap() ** 事件對(duì)象轉(zhuǎn)換成Observable對(duì)象。
flatMap的使用在eg2就有具體的體現(xiàn)。通過(guò)flatMap,返回一個(gè)Observable對(duì)象,并且這個(gè)Observable對(duì)象并不是直接發(fā)送到了Subscriber的回調(diào)方法中。只是將傳入的事件對(duì)象創(chuàng)建一個(gè)Observable,并將其激活,然后Observable匯入到同一個(gè)observable對(duì)象中,這個(gè)observable負(fù)責(zé)將這些事件統(tǒng)一交給了Subscriber的回調(diào)方法。這樣做的好處就是將事件分成了兩級(jí),更加高效。
在eg2中,通過(guò)flatMap獲取到了所有的users數(shù)據(jù),并將他存儲(chǔ)到Observable中。
**3. filter() ** 對(duì)Observable流程的數(shù)據(jù)進(jìn)行過(guò)濾處理。返回值為false不會(huì)發(fā)送到Subscriber參照eg2的寫(xiě)法。eg2就過(guò)濾只剩下user名字為xiaochu的用戶數(shù)據(jù)。
說(shuō)明:filter的實(shí)現(xiàn)是lift()的變換處理,詳細(xì)請(qǐng)參考lift變換部分。另外,filter()返回了一個(gè)新的Observable,因此若不是采用上面這種直接流方式,而是分步調(diào)用方式,需要將新返回的Observable賦給原來(lái)的Observable。
至于更加復(fù)雜的變化以及原理,這里就不做記錄了。有興趣的可以參考博客。http://gank.io/post/560e15be2dca930e00da1083
別看了,看也沒(méi)有源代碼。