隔了好久,終于有時(shí)間更新一下RxJava進(jìn)階的最后一篇文章了获三。前幾個(gè)星期有幸參加了今年的Google I/O(谷歌舉辦的一年一度的開(kāi)發(fā)者大會(huì)食绿,今年在谷歌總部舉行),去了一趟迷人的加州磕潮,于是文章被拖延了翠胰。有興趣的朋友可以看看我上一篇類(lèi)似于日志的google之行文章。
上一次我們介紹了subscribeOn操作符的源碼之景,那么這一次回到正題,把observeOn操作符的用法和源碼過(guò)一遍膏潮。
我們先看看這個(gè)例子:
轉(zhuǎn)換很簡(jiǎn)單锻狗,我們先發(fā)射1,2焕参,3這三個(gè)數(shù)字轻纪,在IO線(xiàn)程進(jìn)行第一次轉(zhuǎn)換,在computation線(xiàn)程進(jìn)行第二次轉(zhuǎn)換龟糕,最后在newThread線(xiàn)程進(jìn)行打印桐磁。我們來(lái)觀(guān)察一下打印結(jié)果。大家可以根據(jù)自己對(duì)observeOn操作符的理解先預(yù)測(cè)一下讲岁。(注意我在發(fā)射元素的地方進(jìn)行了sleep操作我擂,暫停當(dāng)前線(xiàn)程一秒鐘,原因之后會(huì)解釋)
按照我們之前對(duì)map操作符的理解缓艳,這組操作應(yīng)該會(huì)是把對(duì)數(shù)字1先后進(jìn)行轉(zhuǎn)換1校摩,轉(zhuǎn)換2,打印阶淘,然后在對(duì)2做同樣的操作衙吩。
那么我們來(lái)看看結(jié)果是否是這樣?
哈哈溪窒,終于坤塞,我們這次預(yù)測(cè)對(duì)了。的確是按照我們事前所想的那樣把三個(gè)數(shù)字分別打印出來(lái)的澈蚌。observeOn操作符的作用也正是如此摹芙,每次使用observeOn,都會(huì)把這個(gè)observeOn下面的操作包裝在該observeOn指定的線(xiàn)程池中運(yùn)行宛瞄。
還記得上一篇講過(guò)的subscribeOn操作符嘛浮禾,subscribeOn只能作用一次,而observeOn可以作用多次。原因我們接下來(lái)也會(huì)講盈电。
那么我們跟蹤源碼蝴簇,由于我們之前幾篇已經(jīng)講過(guò)RxJava的lift方法和大概的工作原理,這篇我就不多說(shuō)lift的具體作用匆帚,假設(shè)大家已經(jīng)了解相關(guān)知識(shí)了熬词,如果不了解的請(qǐng)從map的源碼分析開(kāi)始。
observeOn操作符會(huì)用OperatorObserveOn改變?cè)械腟ubscriber(大家應(yīng)該還記得原有的subscriber是從哪來(lái)的吧吸重?沒(méi)錯(cuò)這里說(shuō)的原有的subscriber就是從下一級(jí)傳上來(lái)的subscriber)荡澎。
在這個(gè)操作符的call方法里面(大家還記得lift方法會(huì)用操作符的call方法改變?cè)衧ubscriber對(duì)吧?)晤锹,生成新的Susbcriber,ObserveOnSubscriber
新的Subscriber里面彤委,結(jié)構(gòu)有點(diǎn)點(diǎn)復(fù)雜鞭铆,有一個(gè)隊(duì)列,然后把原有的subscriber的引用傳進(jìn)來(lái)焦影,作為自己的成員對(duì)象车遂。
新的subscriber里面,我們重點(diǎn)關(guān)注onNext()方法做了些啥斯辰,因?yàn)槊看伟l(fā)射元素舶担,都是先調(diào)用新的subscriber的onNext()方法的。
新的onNext方法會(huì)先把我們發(fā)射的原始元素添加到隊(duì)列里面(line129),如果發(fā)生錯(cuò)誤彬呻,執(zhí)行onError衣陶,并且返回(line 130 - 131)。假如添加元素成功闸氮,執(zhí)行schedule()剪况。
在創(chuàng)建了一個(gè)新的Action對(duì)象之后,我們把對(duì)象放入一個(gè)Scheduler里面執(zhí)行了蒲跨。如果大家還記得上一篇文章的內(nèi)容译断,應(yīng)該了解這個(gè)Action和Scheduler的關(guān)系。類(lèi)似于Java里面Runnable對(duì)象和ExecutorService一樣或悲,最后Action里面的操作會(huì)被放入線(xiàn)程池孙咪,尋找合適的線(xiàn)程執(zhí)行。
所以其實(shí)最后兜兜轉(zhuǎn)轉(zhuǎn)巡语,最重要的翎蹈,需要執(zhí)行的方法就是這個(gè)pollQueue(),這個(gè)方法從名字就可以看得出來(lái)是要獲取隊(duì)列元素了。而事實(shí)也的確如此捌臊,我們看看pollQueue方法里面最重要的部分杨蛋。
pollQueue方法使用了一個(gè)死循環(huán)(line 182),不斷的去查看queue里面的元素。我們這里重點(diǎn)關(guān)注line 200 -202, 在把隊(duì)列里面的第一個(gè)元素取出之后逞力,我們交給child曙寡,也就是包裝進(jìn)來(lái)的,來(lái)自下一級(jí)的subscriber寇荧,讓它來(lái)執(zhí)行它的onNext()举庶。這樣,我們的原始的Observable的第一個(gè)元素就這樣處理完畢了揩抡。
這整一個(gè)pollQueue方法户侥,因?yàn)楸话b在A(yíng)ction對(duì)象里面,而Action對(duì)象又會(huì)被放進(jìn)Scheduler峦嗤,也就是RxJava包裝過(guò)之后的ExecutorService里面執(zhí)行蕊唐,所以它是會(huì)運(yùn)行在我們指定的那個(gè)線(xiàn)程里面的。我們可以在debug模式下面驗(yàn)證一下烁设。
所以總結(jié)一下的話(huà)替梨,使用observeOn,會(huì)在原有的Observable發(fā)射元素的時(shí)候,將元素依次添加到一個(gè)隊(duì)列中装黑,并異步的(使用一個(gè)新的線(xiàn)程去執(zhí)行)不停的去獲取隊(duì)列的第一個(gè)元素副瀑,使用下一級(jí)的subscriber處理(onNext())該元素。
那么我們?cè)賹⑺季S拓展一下疚颊,如果在observeOn后面跟的不是subscribe()方法狈孔,而是一個(gè)map方法。會(huì)有什么不同呢串稀?
如果是map的話(huà)除抛,大家應(yīng)該還記得,下一級(jí)傳上來(lái)的subscriber也不會(huì)是我們?cè)趕ubscribe()方法里面?zhèn)鬟M(jìn)來(lái)的subscriber母截,而是經(jīng)過(guò)OperatorMap改造過(guò)的subscriber了到忽。
也就是說(shuō),pollQueue方法的line202清寇,child成員就是OperatorMap改造后的subscriber喘漏,那么執(zhí)行的onNext()也就是上圖line52-58的這個(gè)onNext方法了,line 54中华烟,我們會(huì)在observeOn操作符里面的定義的線(xiàn)程中翩迈,執(zhí)行transformer.call(t),也就是說(shuō),我們保證了map操作符里面的call()方法(就是我們進(jìn)行類(lèi)型轉(zhuǎn)換的方法)是執(zhí)行在observeOn所定義的線(xiàn)程中盔夜,然后在執(zhí)行o.onNext(),這個(gè)o對(duì)象负饲,又是下一級(jí)傳上來(lái)的subscriber堤魁。
所以,假如我們遇到了多次的ObserveOn+Map的轉(zhuǎn)換的話(huà)返十,發(fā)射元素的流程就變成這樣了:
回到我們開(kāi)頭的問(wèn)題妥泉,為什么我們要在原始的Observable發(fā)射元素之間,暫停當(dāng)前線(xiàn)程一秒洞坑?
理由很簡(jiǎn)單盲链,因?yàn)槿绻贿@樣做的話(huà),我們的map轉(zhuǎn)換會(huì)被放到ExectuorService里面執(zhí)行迟杂,是異步執(zhí)行的刽沾,所以很有可能會(huì)出現(xiàn);第一層轉(zhuǎn)換都執(zhí)行完畢排拷,才執(zhí)行下一層轉(zhuǎn)換侧漓,最后日志的打印結(jié)果就不是我們預(yù)測(cè)的那樣了溢豆。
大家想象一下彩库,把上圖的第二和第三層向右方無(wú)限拉長(zhǎng)此衅,拉倒第三map之后哮笆,就會(huì)出現(xiàn)RxJava似乎是在處理完一層之后再處理下一層的錯(cuò)覺(jué)。
那么到此為止阎肝,RxJava進(jìn)階的所有專(zhuān)題都結(jié)束了,其實(shí)關(guān)于RxJava,還有很多東西我們都沒(méi)接觸牛欢,例如怎么handle Subscription等等。這也就留給有興趣的同學(xué)自己慢慢摸索了淆游。接下來(lái)我會(huì)專(zhuān)心準(zhǔn)備Android Tv的教程給大家傍睹。希望有興趣開(kāi)發(fā)安卓TV應(yīng)用的同學(xué)們留意一下 ;)
另外,很高興勇士贏(yíng)球犹菱,作為一個(gè)庫(kù)里球迷拾稳,我忍不住上個(gè)圖: