通過(guò)上一篇搞莺,想必大家都對(duì)RxJava有了清楚的認(rèn)識(shí)更哄,那么接下來(lái)我們來(lái)講RxJava的線程控制<code>Schduler</code>邢笙。
一啸如、Scheduler的API
默認(rèn)情況下,RxJava遵循線程不變?cè)瓌t氮惯。即:在哪個(gè)線程調(diào)用subscribe()方法叮雳,就在哪個(gè)線程生產(chǎn)事件,在哪個(gè)線程生產(chǎn)事件妇汗,就在哪個(gè)線程消費(fèi)事件帘不。如果需要切換線程,就需要用到<code>Scheduler</code>(調(diào)度器)杨箭。
在RxJava中寞焙,<code>Scheduler</code>相當(dāng)于線程控制器,RxJava通過(guò)它來(lái)指定每一段代碼運(yùn)行在什么線程中互婿。RxJava內(nèi)置了幾個(gè)<code>Scheduler</code>捣郊,適合大多數(shù)使用場(chǎng)景:
- <code> Schedulers.immediate()</code>: 直接在當(dāng)前線程運(yùn)行,相當(dāng)于不指定線程擒悬。這是默認(rèn)的Scheduler模她。
- <code>Schedulers.newThread()</code>: 總是啟用新線程稻艰,并在新線程執(zhí)行操作懂牧。
- <code>Schedulers.io()</code>: I/O 操作(讀寫文件、讀寫數(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)建不必要的線程途事。
- <code>Schedulers.computation()</code>: 計(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 專用的<code>AndroidSchedulers.mainThread()</code>奏夫,它指定的操作將在 Android 主線程運(yùn)行怕篷。
有了這幾個(gè) <code>Scheduler</code> ,就可以使用 subscribeOn()和 observeOn()兩個(gè)方法來(lái)對(duì)線程進(jìn)行控制了酗昼。
<code>subscribeOn()</code>:* 指定subscribe()(訂閱/注冊(cè))所發(fā)生的線程廊谓,即 Observable.OnSubscribe被激活時(shí)所處的線程÷橄鳎或者叫做事件產(chǎn)生的線程蹂析。*
<code>observeOn()</code>: 指定Subscriber(觀察者)所運(yùn)行在的線程〉牛或者叫做事件消費(fèi)的線程电抚。
文字?jǐn)⑹隹倸w難理解,上代碼:
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io()) // 指定 subscribe() 發(fā)生在 IO 線程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回調(diào)發(fā)生在主線程(即 Action1對(duì)象)
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer number) {
Log.d(tag, "number:" + number);
}
});
上面這段代碼中竖共,由于 <code>subscribeOn(Schedulers.io())</code>的指定蝙叛,被創(chuàng)建的事件的內(nèi)容 1、2公给、3借帘、4將會(huì)在 IO 線程發(fā)出;而由于<code>observeOn(AndroidScheculers.mainThread())</code> 的指定淌铐,因此 subscriber數(shù)字的打印將發(fā)生在主線程 肺然。事實(shí)上,這種在subscribe()之前寫上兩句 <code>subscribeOn(Scheduler.io())</code>和 <code>observeOn(AndroidSchedulers.mainThread())</code>的使用方式非常常見(jiàn)腿准,它適用于多數(shù)的 『后臺(tái)線程取數(shù)據(jù)际起,主線程顯示』的程序策略拾碌。
前面講到了,可以利用 subscribeOn()結(jié)合 observeOn()來(lái)實(shí)現(xiàn)線程控制街望,讓事件的產(chǎn)生和消費(fèi)發(fā)生在不同的線程校翔。那么可以多切換幾次線程嗎?那你就要了解RxJava變換的原理了灾前。
二防症、Scheduler的原理
其實(shí), subscribeOn() 和 observeOn()的內(nèi)部實(shí)現(xiàn)哎甲,也是用的 lift()蔫敲。具體看圖(不同顏色的箭頭表示不同的線程):
<code>subscribeOn()</code>原理圖:
<code>observeOn()</code>原理圖:
從圖中可以看出,<code>subscribeOn()</code> 和 <code>observeOn()</code>都做了線程切換的工作(圖中的 "schedule..." 部位)炭玫。不同的是燕偶, <code>subscribeOn()</code>的線程切換發(fā)生在 OnSubscribe中,即在它通知上一級(jí) OnSubscribe時(shí)础嫡,這時(shí)事件還沒(méi)有開(kāi)始發(fā)送指么,因此 <code>subscribeOn()</code>的線程控制可以從事件發(fā)出的開(kāi)端就造成影響;而 <code>observeOn()</code>的線程切換則發(fā)生在它內(nèi)建的 Subscriber中榴鼎,即發(fā)生在它即將給下一級(jí)Subscriber 發(fā)送事件時(shí)伯诬,因此 <code>observeOn()</code>控制的是它后面的線程。
最后巫财,我用一張圖來(lái)解釋當(dāng)多個(gè) <code>subscribeOn()</code>和 <code>observeOn()</code>混合使用時(shí)盗似,線程調(diào)度是怎么發(fā)生的(由于圖中對(duì)象較多,相對(duì)于上面的圖對(duì)結(jié)構(gòu)做了一些簡(jiǎn)化調(diào)整):
圖中共有 5 處含有對(duì)事件的操作平项。由圖中可以看出赫舒,①和②兩處受第一個(gè) <code>subscribeOn()</code>影響,運(yùn)行在紅色線程闽瓢;③和④處受第一個(gè)<code>observeOn()</code>的影響接癌,運(yùn)行在綠色線程;⑤處受第二個(gè) <code>onserveOn()</code>影響扣讼,運(yùn)行在紫色線程缺猛;而第二個(gè) <code>subscribeOn()</code>,由于在通知過(guò)程中線程就被第一個(gè) <code>subscribeOn()</code>
截?cái)嗤址虼藢?duì)整個(gè)流程并沒(méi)有任何影響荔燎。這里也就回答了前面的問(wèn)題:當(dāng)使用了多個(gè)<code>subscribeOn()</code>的時(shí)候,只有第一個(gè) <code>subscribeOn()</code>起作用销钝。