本篇文章主要介紹線(xiàn)程調(diào)度器,通過(guò)對(duì)線(xiàn)程調(diào)度器的了解,方便我們更好的處理異步操作,在合適的場(chǎng)景選擇合適的線(xiàn)程。同時(shí),結(jié)合上篇文章,我們就初步掌握了 RxJava 2.x的基本操作并可以應(yīng)用在我們的項(xiàng)目中。在本篇文章的后半部分,會(huì)具體展示RxJava 2.x的使用。
Scheduler簡(jiǎn)介
在不指定線(xiàn)程的情況下, RxJava 遵循的是線(xiàn)程不變的原則,即:在哪個(gè)線(xiàn)程調(diào)用 subscribe(),就在哪個(gè)線(xiàn)程生產(chǎn)事件;在哪個(gè)線(xiàn)程生產(chǎn)事件,就在哪個(gè)線(xiàn)程消費(fèi)事件。如果需要切換線(xiàn)程,就需要用到 Scheduler (調(diào)度器)。
在RxJava 中,Scheduler,相當(dāng)于線(xiàn)程控制器,RxJava 通過(guò)它來(lái)指定每一段代碼應(yīng)該運(yùn)行在什么樣的線(xiàn)程。RxJava 已經(jīng)內(nèi)置了幾個(gè) Scheduler ,它們已經(jīng)適合大多數(shù)的使用場(chǎng)景。
Scheduler 的 API
● Schedulers.immediate(): 直接在當(dāng)前線(xiàn)程運(yùn)行,相當(dāng)于不指定線(xiàn)程。這是默認(rèn)的 Scheduler。
●Schedulers.newThread(): 總是啟用新線(xiàn)程,并在新線(xiàn)程執(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ù)量上限的線(xiàn)程池,可以重用空閑的線(xiàn)程,因此多數(shù)情況下 io() 比 newThread() 更有效率。不要把計(jì)算工作放在 io() 中,可以避免創(chuàng)建不必要的線(xiàn)程。
**●Schedulers.computation(): **計(jì)算所使用的 Scheduler。這個(gè)計(jì)算指的是 CPU 密集型計(jì)算,即不會(huì)被 I/O 等操作限制性能的操作,例如圖形的計(jì)算。這個(gè) Scheduler 使用的固定的線(xiàn)程池,大小為 CPU 核數(shù)。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時(shí)間會(huì)浪費(fèi) CPU。
● Android 還有一個(gè)專(zhuān)用的** AndroidSchedulers.mainThread()**,它指定的操作將在 Android 主線(xiàn)程運(yùn)行。
有了這幾個(gè) Scheduler ,就可以使用 subscribeOn() 和 observeOn() 兩個(gè)方法來(lái)對(duì)線(xiàn)程進(jìn)行控制了。subscribeOn(): 指定Observable(被觀察者)所在的線(xiàn)程,或者叫做事件產(chǎn)生的線(xiàn)程。 * observeOn(): 指定 Observer(觀察者)所運(yùn)行在的線(xiàn)程,或者叫做事件消費(fèi)的線(xiàn)程。
下面用代碼展示下線(xiàn)程調(diào)度的使用:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
Log.d("所在的線(xiàn)程:",Thread.currentThread().getName());
Log.d("發(fā)送的數(shù)據(jù):", 1+"");
e.onNext(1);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) /
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d("所在的線(xiàn)程:",Thread.currentThread().getName());
Log.d("接收到的數(shù)據(jù):", "integer:" + integer);
}
});
01-19 10:06:38.275 27734-27783/? D/所在的線(xiàn)程:: RxCachedThreadScheduler-1
01-19 10:06:38.275 27734-27783/? D/發(fā)送的數(shù)據(jù):: 1
01-19 10:06:38.285 27734-27734/? D/所在的線(xiàn)程:: main
01-19 10:06:38.285 27734-27734/? D/接收到的數(shù)據(jù):: integer:1
可以看到,Observable(被觀察者)發(fā)送事件的線(xiàn)程的確改變了, 是在一個(gè)叫 RxCachedThreadScheduler-1的線(xiàn)程中發(fā)送的事件, 而Observer(觀察者)仍然在主線(xiàn)程中接收事件。由此我們實(shí)現(xiàn)了線(xiàn)程調(diào)度的操作,可以在此基礎(chǔ)上盡情的進(jìn)行異步操作。
下面來(lái)介紹一個(gè)具體的使用場(chǎng)景。
RxJava 2.x 網(wǎng)絡(luò)請(qǐng)求使用
Android中有多種網(wǎng)絡(luò)請(qǐng)求庫(kù), Retrofit便是其中的佼佼者,它的優(yōu)勢(shì)之一便是它支持RxJava的方式來(lái)調(diào)用。我們便以Retrofit進(jìn)行網(wǎng)絡(luò)請(qǐng)求,RxJava進(jìn)行異步處理,兩者結(jié)合來(lái)講解RxJava在網(wǎng)絡(luò)請(qǐng)求中的具體使用。
本例中 我們使用聚合數(shù)據(jù)中的全國(guó)天氣數(shù)據(jù),獲得城市信息。
接口url:http://v.juhe.cn/weather/citys?key=.... 其中key是你申請(qǐng)時(shí)聚合數(shù)據(jù)給你的密鑰。
具體請(qǐng)求的返回?cái)?shù)據(jù)形式如下:
下面以上述數(shù)據(jù)簡(jiǎn)單講解一下Retrofit的基本用法。
要使用Retrofit,先在Gradle中添加配置:
//Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
//Gson converter
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//Okhttp
compile 'com.squareup.okhttp3:okhttp:3.5.0'
//RxJava adapter
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
定義Api接口:
public interface Api {
@GET("citys")
Observable<AllCity> getAllCity(@Query("key") String key);
}
創(chuàng)建一個(gè)Retrofit客戶(hù)端:
private static Retrofit create() {
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(10, TimeUnit.SECONDS);
builder.connectTimeout(9, TimeUnit.SECONDS);
return new Retrofit.Builder().baseUrl(baseUrl)
.client(builder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
接下來(lái)就可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求:
Retrofit retrofit = create();
Api api = retrofit.create(Api.class);
Observable<AllCity> observable = api.getAllCity(appkey);
observable.subscribeOn(Schedulers.io())
.flatMap(new Function<AllCity, ObservableSource<City>>() {
@Override
public ObservableSource<City> apply(AllCity city) throws Exception {
ArrayList<City> result = city.getResult();
return Observable.fromIterable(result);
}
})
.filter(new Predicate<City>() {
@Override
public boolean test(City city) throws Exception {
String id = city.getId();
if(Integer.parseInt(id)<5){
return true;
}
return false;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<City>() {
@Override
public void accept(City city) throws Exception {
System.out.println(city);
}
});
01-19 13:28:56.952 13218-13218/com.lvr.rxjavalearning I/System.out: City{id='1', province='北京', city='北京', district='北京'}
01-19 13:28:56.952 13218-13218/com.lvr.rxjavalearning I/System.out: City{id='2', province='北京', city='北京', district='海淀'}
01-19 13:28:56.952 13218-13218/com.lvr.rxjavalearning I/System.out: City{id='3', province='北京', city='北京', district='朝陽(yáng)'}
01-19 13:28:56.952 13218-13218/com.lvr.rxjavalearning I/System.out: City{id='4', province='北京', city='北京', district='順義'}
調(diào)用Api接口方法,返回一個(gè)Observable(被觀察者)對(duì)象,然后當(dāng)subscribe()訂閱后,就可以在IO線(xiàn)程中執(zhí)行網(wǎng)絡(luò) 請(qǐng)求操作,然后進(jìn)行轉(zhuǎn)換過(guò)濾,最終Observer(觀察者)對(duì)象在UI線(xiàn)程中獲得城市id在1-4之間的城市信息。
其中請(qǐng)求返回的數(shù)據(jù)是json形式,AllCity類(lèi)包含所有的返回?cái)?shù)據(jù),具體代碼如下:
public class AllCity {
private String error_code;
private String reason;
private String resultcode;
private ArrayList<City> result;
//省略getter,setter方法
}
ArrayList集合中封裝了所有城市的信息,City類(lèi)包含城市詳細(xì)信息,具體代碼如下:
public class City {
/**
* id : 1
* province : 北京
* city : 北京
* district : 北京
*/
private String id;
private String province;
private String city;
private String district;
//省略getter,setter,toString方法
}
本例中,我們假設(shè)Observer(觀察者)需要id號(hào)在1-4之間的城市信息,我們就可以先使用flatMap()操作符先將封裝所有信息的AllCity中提取出城市信息集合,然后轉(zhuǎn)換成一個(gè)新的Observable(被觀察者)進(jìn)行傳遞,然后使用filter()進(jìn)行過(guò)濾,過(guò)濾出符合要求的城市信息,最終傳遞給Observer(觀察者),讓其在UI線(xiàn)程接收數(shù)據(jù),然后更新UI。整個(gè)過(guò)程完成了網(wǎng)絡(luò)請(qǐng)求,同時(shí)進(jìn)行異步操作,防止阻塞UI線(xiàn)程。
以上僅僅以實(shí)例介紹RxJava的基礎(chǔ)使用,RxJava的功能遠(yuǎn)不止于此。不過(guò)掌握了以上的技能,我們已經(jīng)可以在我們的項(xiàng)目中應(yīng)用RxJava進(jìn)行異步操作了。關(guān)于一些RxJava中的細(xì)節(jié)及其他相關(guān)技術(shù)還需要慢慢積累。
下面我們另一個(gè)重要的概念Disposable。當(dāng)Observer(觀察者)與Observable(被觀察者)通過(guò)subscribe()建立連接后,事件可以進(jìn)行傳遞。當(dāng)發(fā)生一些其他情況,不得不斷開(kāi)兩者之間的連接時(shí),該怎么操作?這個(gè)時(shí)候就該Disposable上場(chǎng)了。
Disposable簡(jiǎn)介及使用
Disposable簡(jiǎn)介
Disposable, 這個(gè)單詞的字面意思是一次性用品,用完即可丟棄的。在RxJava中,用它來(lái)切斷Observer(觀察者)與Observable(被觀察者)之間的連接,當(dāng)調(diào)用它的dispose()方法時(shí), 它就會(huì)將Observer(觀察者)與Observable(被觀察者)之間的連接切斷, 從而導(dǎo)致Observer(觀察者)收不到事件。
下面我們就該考慮如何來(lái)獲得Disposable對(duì)象?
Disposable的作用是切斷連接,確切地講是將Observer(觀察者)切斷,不再接收來(lái)自被觀察者的事件,而被觀察者的事件卻仍在繼續(xù)執(zhí)行。
因此Disposable的對(duì)象通過(guò)觀察者獲得,具體分為兩種方式。
Disposable對(duì)象的獲得
1.Observer接口
Observer<String> observer = new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
};
通過(guò)創(chuàng)建Observer接口,當(dāng)訂閱后,建立與Observable的聯(lián)系,onSubscribe(Disposable d)中便可以獲得Disposable對(duì)象。
2.Consumer等其他函數(shù)式接口
Disposable disposable = Observable.just("你好").subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
}
});
當(dāng)subscribe()后直接返回一個(gè)Disposable 對(duì)象
獲得了Disposable對(duì)象后,我們便可以調(diào)用dispose()方法,在恰當(dāng)?shù)臅r(shí)機(jī),斷開(kāi)連接,停止接收Observable(被觀察者)發(fā)送的事件。
注意:當(dāng)切斷被觀察者與觀察者之間的聯(lián)系,Observable(被觀察者)的事件卻仍在繼續(xù)執(zhí)行。
另外,補(bǔ)充一下onNext()、onComplete()和onError()事件的發(fā)送規(guī)則。
具體規(guī)則:
Observable(被觀察者)可以發(fā)送無(wú)限個(gè)onNext, Observer(觀察者)也可以接收無(wú)限個(gè)onNext.
當(dāng)Observable(被觀察者)發(fā)送了一個(gè)onComplete后, Observable(被觀察者)中onComplete之后的事件將會(huì)繼續(xù)發(fā)送, 而Observer(觀察者)收到onComplete事件之后將不再繼續(xù)接收事件.
當(dāng)Observable(被觀察者)發(fā)送了一個(gè)onError后, Observable(被觀察者)中onError之后的事件將繼續(xù)發(fā)送, 而Observer(觀察者)收到onError事件之后將不再繼續(xù)接收事件.
Observable(被觀察者)可以不發(fā)送onComplete或onError.
最為關(guān)鍵的是onComplete和onError必須唯一并且互斥, 即不能發(fā)多個(gè)onComplete, 也不能發(fā)多個(gè)onError, 也不能先發(fā)一個(gè)onComplete, 然后再發(fā)一個(gè)onError, 反之亦然
注: 關(guān)于onComplete和onError唯一并且互斥這一點(diǎn), 是需要自行在代碼中進(jìn)行控制, 如果你的代碼邏輯中違背了這個(gè)規(guī)則, 并不一定會(huì)導(dǎo)致程序崩潰. 比如發(fā)送多個(gè)onComplete是可以正常運(yùn)行的, 依然是收到第一個(gè)onComplete就不再接收了, 但若是發(fā)送多個(gè)onError, 則收到第二個(gè)onError事件會(huì)導(dǎo)致程序會(huì)崩潰。
以上就是本篇文章的全部?jī)?nèi)容,結(jié)合上一篇文章,已經(jīng)可以靈活使用RxJava了。在下篇文章中,將會(huì)介紹RxJava中新增加的內(nèi)容:Flowable及backpressure。