Rxjava實踐: 把混亂的WORKFLOW擼成串吧

上個月做的事情比較多:改改iOS bug拷恨,學(xué)python超陆,把項目重構(gòu)成MVP书蚪,深入使用Rxjava喇澡。

這次來說說Rxjava,通過還原一個真實的開發(fā)過程殊校,來感受下rxjava的便利之處晴玖。

巨坑從來都是由小坑慢慢塌陷的

先來看下一段最普通的代碼

rx01.png

在沒有特殊需求的情況下,代碼就這么簡單箩艺。你可以理解為窜醉,獲取一個目錄下的所有文件,將它們一個個傳到服務(wù)器上去艺谆。

看起來好像是沒什么問題榨惰,一個for循環(huán)搞定。一個task失敗了不影響另一個task静汤。每個task run在一個單獨的子線程琅催。

之前rxjava使用場景只局限于和Retrofit一起用。沒過多的使用操作符虫给。因此在uploadFile(path)方法中就是最簡單的Retrofit+Rxjava上傳文件藤抡。rxjava就切換了下線程。

對于寫慣java的人抹估,這么寫是沒什么問題的缠黍。但如果深入使用過rxjava之后,這么寫就非常別扭了药蜻〈墒剑看到for loop了替饿,你不想將它改成Observable.from()嘛?

把能看見的都改成stream吧

getFileList()方法是獲取sd卡中data包下所有以loc為后綴的files贸典。

workflow分三步:

  1. locate to data dir
  2. list files under data dir
  3. filter files with .loc suffix

換成rxjava非常容易

  1. 先發(fā)射一個data目錄路徑
  2. 需求是多次上傳文件视卢,得用flatMap將data映射成一個Observable<File[]>
    2.1 當(dāng)然你可以選擇直接listFile(filter),但這樣回調(diào)又套回調(diào)廊驼,不是很好看据过。
    2.2 用filter操作符將發(fā)射來的File[]過濾

比如像2.1這樣寫

rx02.png

或者像2.2這樣寫


rx03.png

注意,在flatMap中又用from()操作符將File[]變換成一個OnSubscribeFromArray類型的Observable在內(nèi)部通過for循環(huán)一個個發(fā)射妒挎。(感謝一位朋友指正绳锅,之前理解錯誤,以為是發(fā)射多個Observable饥漫。不看源碼真是稀里糊涂的)

假如你的API接口可以接收多個文件榨呆,其實也不用這樣寫。直接在flatMap中拼接RequestBody庸队,調(diào)用API請求就可以了。比如像下圖這樣寫:


rx04.png

無奈需求是上傳loc文件同時還會再帶上一個sensor文件闯割,所以就不能像上述這樣寫彻消。

產(chǎn)品說:需求變了~

接下來的workflow就很有趣了。現(xiàn)在有了多個Observable<File>宙拉,一個個上傳就是了宾尚。

如果不考慮隊列,不考慮無網(wǎng)或上傳失敗情況谢澈。完全再來一個flatMap將Observable<File>變換為Observable<Response<HashMap>>就可以了煌贴。比如:


rx05.png

但現(xiàn)在的需求是,隊列上傳文件锥忿,也就是說牛郑,必須一個任務(wù)完成(成功|失敗)后才能進行下一個任務(wù)。這樣用flatMap就不可以了敬鬓。(其實后來我考慮過這個問題淹朋,線程的調(diào)度本質(zhì)還是由我聲明出來的線程池來決定的,如果用Schedulers.newThread()钉答,那就會創(chuàng)建多個子線程础芍。但如果用Schedulers.from(Executors.newSingleThreadExecutor())呢?)

需求總是多變的数尿,好在有rxjava可以隨意變換仑性。來吧,我們看看不用單個線程池右蹦,如何實現(xiàn)隊列诊杆。

不能隨意套路歼捐,坑的是自己

之前學(xué)習(xí)rxjava時,看過很多在android中高度使用rxjava的文章刽辙。有一個操作符很有意思-> concat()

The Concat operator concatenates the output of multiple Observables so that they act like a single Observable, with all of the items emitted by the first Observable being emitted before any of the items emitted by the second Observable (and so forth, if there are more than two).

即將多個Observables串起成一個Observable窥岩,直到一個執(zhí)行完畢后再執(zhí)行下一個。

我們可以將這個concat()應(yīng)用在讀取緩存還是請求服務(wù)器, 如果緩存有數(shù)據(jù)宰缤,那就不用請求服務(wù)器了颂翼。

Observable<Data> cache;
Observable<Data> server;
Observable.concat(cache, server)
            .first()

這個也可以用在隊列上傳文件場景上咯。but慨灭,concat()是創(chuàng)建型操作符朦乏,再次變換就不能使用了。不過可以用concatMap(),

Returns a new Observable that emits items resulting from applying a function that you supply to each item emitted by the source Observable, where that function returns an Observable, and then emitting the items that result from concatenating those resulting Observables.

直接看代碼吧

rx06.png

這段寫的特別扭氧骤,為什么又要在一個Observable里又創(chuàng)建一個retrofit相關(guān)的Observable?當(dāng)時想的是筹陵,因為要在upload成功后得刪除文件啊。如果把subscribe放到外層去朦佩,那接收到的全是服務(wù)器response,不知道當(dāng)前的response屬于哪一個file upload语稠。所以我就又寫了次變換宋彼。(這里肯定可以優(yōu)化的,寫的太挫)

在concatMap中接收到from()發(fā)射來的一個Observable输涕,變換成Retrofit請求慨畸,當(dāng)Subscriber標記為onCompleted后再去執(zhí)行下一個Observable莱坎。

到這里還沒完,假如無網(wǎng)絡(luò)又或者服務(wù)器異常先口。在第一個Observable就會失敗型奥,此時還需要繼續(xù)請求嗎碉京?很有可能后面的Observable也都不成功。那加個判斷吧谐宙。concat()可以和first()一起用。concatMap()也是可以的。

rx07.png

If you are only interested in the first item emitted by an Observable, or the first item that meets some criteria, you can filter the Observable with the First operator.

如果first() -> return true; 這樣只取到目前的這個Observable垢箕,后續(xù)的不執(zhí)行了兑巾。

也就是說,只有在上傳成功時return false蒋歌,繼續(xù)執(zhí)行下一個Observable。否則就return true停止修档。

覺醒分割線

我想之前肯定是被concat(cache, db, server).first()整懵逼了府框,一心去套,才寫了上面這么二的代碼的迫靖。等等,容我換個姿勢撕予。

rx10.png

看蜈首,對請求結(jié)果map變換一次就可以啦欠母,如果成功刪除相關(guān)文件,不成功就是個異常了赏淌。Observable.error()踩寇。這樣就跳出了concatMap,也就是說六水,當(dāng)異常發(fā)生時會停止后續(xù)的文件上傳。這樣first()也不需要啦睛榄。除非還有其他額外的停止flag要判斷想帅。

到這里整個workflow就被rxjava梳理完畢了。是不是很有趣?我們來看下代碼全過程咧欣。

rx08.png

還剩最后一個問題:線程調(diào)度轨帜。

之前一直都沒寫線程調(diào)度的地方。subscribeOn放在哪里比較好哮兰?

需求是:在主線程listFile拿到目錄下的所有文件梢什,然后在子線程一個個隊列上傳文件,執(zhí)行完畢后再切換到主線程彈dialog告知結(jié)果嗡午。

這樣來說,每個文件上傳時不需要切換線程狸演,所以調(diào)用retrofit的地方是不需要subscribeOn僻他。如果執(zhí)意要在uploadTrip()后加上subscribeOn(io),也不是不可以吨拗。只是每個上傳task都在一個新的線程里執(zhí)行的。但實際上哨鸭,我們的文件上傳是個隊列娇妓,完全可以一直在同一個線程里執(zhí)行。所以我在了flatMap后observeOn(io)哈恰。最終執(zhí)行的log如下圖

rx09.png

之前亂嘗試,寫了兩個subscribeOn()着绷,雖然邏輯是對的,但思想上來說是observeOn多次夸楣,subscribeOn一次,幸好有位朋友提醒石洗,才改成了上圖。

好了讲衫,混亂的workflow總算擼成串了孵班。平時看相關(guān)文章總覺得很簡單,無非就是幾個操作符拼接在一起篙程,做了線程切換虱饿。不好理解的就是鏈式思維的轉(zhuǎn)換還有一些操作符:compose transformer等。等到真正應(yīng)用到項目場景中氮发,著實折騰了不少。比如不用flatMap仇祭,改為concatMap颈畸。比如線程調(diào)度。比如放棄使用retrofit+rxjava套路眯娱,重新認識reactive等。

總得來說,當(dāng)理解了rxjava的鏈式思維并對一些復(fù)雜的邏輯重構(gòu)之后贰谣,還是會愛上的。

參考閱讀

  1. 理解操作符百宇,還是看官網(wǎng)最佳ReactiveX
  2. 感謝小鄧子幫忙梳理流程秘豹。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市啄刹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌誓军,老刑警劉巖袱讹,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昵时,死亡現(xiàn)場離奇詭異,居然都是意外死亡壹甥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門浦译,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俄占,“玉大人,你說我怎么就攤上這事渤弛∩醮” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵晴氨,是天一觀的道長碉输。 經(jīng)常有香客問我,道長敷钾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任挠锥,我火速辦了婚禮侨赡,結(jié)果婚禮上粱侣,老公的妹妹穿的比我還像新娘蓖宦。我一直安慰自己,他們只是感情好尔店,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布主慰。 她就那樣靜靜地躺著,像睡著了一般该肴。 火紅的嫁衣襯著肌膚如雪藐不。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天涎嚼,我揣著相機與錄音挑秉,去河邊找鬼法梯。 笑死犀概,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的铛绰。 我是一名探鬼主播产喉,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼尘颓!你這毒婦竟也來了晦譬?” 一聲冷哼從身側(cè)響起互广,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤卧土,失蹤者是張志新(化名)和其女友劉穎像樊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颤霎,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡涂滴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年柔纵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搁料。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖霸琴,靈堂內(nèi)的尸體忽然破棺而出昭伸,到底是詐尸還是另有隱情,我是刑警寧澤勋乾,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布辑莫,位于F島的核電站,受9級特大地震影響各吨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜横浑,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一屉更、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瑰谜,春花似錦树绩、人聲如沸隐轩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悴灵。三九已至,卻和暖如春称勋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背空厌。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工银酬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像宠哄,于是被迫代替她去往敵國和親嗤攻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容