摘要:來總結(jié)一下我遇到的坑,或者說我為什么不在推薦使用。但是功利的看,在解決異步處理這個問題上,的確是投入高,收獲少。這種在輕量級應(yīng)用,或者一些小型異步處理比如數(shù)據(jù)埋點等等行為中,都顯得過于龐大。
距離上一次更新也有一段時間了,其實這篇文章我早就想寫,礙于一直沒來得及總結(jié)(懶)。所以一直沒有成文。來總結(jié)一下我RxJava遇到的坑,或者說我為什么不在推薦使用RxJava。 相信熟悉或者關(guān)注我的朋友,絕大多數(shù)都是因為RxJava。所以看到這個標(biāo)題你已經(jīng)會驚訝。 作為RxJava堅定的擁護(hù)者,或者說自干五?為什么突然不再支持RxJava了呢?
先講講歷史在我的文章中已經(jīng)講過很多次RxJava誕生之初就是因為異步。再后來借鑒LINQ的思想借用Monad的力量使得 Rx可以使用操作符進(jìn)行組合將各種復(fù)雜的請求簡單化。 可以說,RxJava的設(shè)計初衷就是圍繞著Asyhconization和Composition。當(dāng)年的Netflix也是為了增加服務(wù)器的性能和吞吐量來編寫RxJava并開源。才使得RxJava問世。詳細(xì)關(guān)于這段可以參考我的知乎回答:你會在實際工作中使用 rxjava 嗎?
再聊聊異步在那個RxJava剛剛火爆的年代,那是一個荒蠻的年代。我們在異步方面資源匱乏,手頭僅有ThreadPool,AsyncTask和Handler這些基礎(chǔ)封裝的異步庫。所以當(dāng)我們看見RxJava這個新奇的小玩意,當(dāng)我們看到異步還可以這么簡單,輕而易舉的解決Concurrency問題。我們當(dāng)然如獲至寶。 而我們現(xiàn)在選擇就更多了,無論是Java 8本身提供的CompletableFuture。還是后起之秀Kotlin上的Coroutine,還有Android 上官方提供的LiveData(這里說下: 雖然本質(zhì)上線程管理仍需用戶自己,但是常見的比如Room數(shù)據(jù)庫,Retrofit等等都有現(xiàn)成的LiveDataAdapter,實際上并不需要我們過多操心線程問題)。 相比之下,RxJava優(yōu)勢并不那么明顯,相反劣勢卻很突出。
RxJava 門檻太高相信多數(shù)Android開發(fā)者并沒有了解過或者說深入了解過(我自己也沒深入了解過)函數(shù)式相關(guān)的知識。但是如果不了解這些,那么而幾乎可以說不可能融會貫通RxJava的一些概念。 舉個例子,一個很著名的Googler:Yigit Boyar。也就是每次IO的那個大胡子,他的代表作有很多。比如RecyclerView,再比如Architecture Component。這樣一個Android界名人,水平怎么也有平均以上。但是他在實現(xiàn)LiveData和RxJava適配的時候,同樣出現(xiàn)了由于理解上出的問題,造成錯誤的實現(xiàn)方式。 RxJava的門檻過于高,就連我自己推廣這么久,自己也不敢說對RxJava了解有多深刻。經(jīng)常在常見操作符的使用中出現(xiàn)了或多或少的unexpected behavior。 再者,無論國內(nèi)國外的RxJava教程水平都參差不齊。新手很難鑒別哪些人說的是對的哪些人說的是錯誤的。在這樣魚龍混雜的條件下學(xué)好這個高門檻的異步庫更是變得難上加難。很多教程在自己沒有精通的情況下,很容易誤導(dǎo)其他人(包括我自己的文章)。
投入高,收獲少雖然這點存疑,因為我自己鉆研RxJava之后確實覺得收獲很大,尤其是經(jīng)由RxJava窺探了函數(shù)式的大門。但是功利的看,RxJava在解決異步處理這個問題上,的確是投入高,收獲少。 異步問題是Android開發(fā)必不可少的一個環(huán)節(jié),可以說掌握異步應(yīng)該是成為入門Android開發(fā)的敲門磚。而RxJava歸根到底是通過響應(yīng)式的方式配合Monad來解決異步問題。但是僅僅為了解決異步問題,學(xué)習(xí)并精通RxJava并不是必不可少的。相反,精通RxJava需要大量時間和精力,在現(xiàn)在異步編程逐步完善的情況下,完全沒有必要。
你永遠(yuǎn)無法預(yù)測你同事的RxJava水平上面幾點可能有點抽象,而這點和接下來的幾點都是我在實際工作中遇到的實際情況。首先就是你并不能預(yù)測或者要求你的同事RxJava到達(dá)什么樣的水平。 我之前的公司使用了一個簡單的類redux框架。其中RxJava是核心部分,他承載了中間render層和view層的連接。具體關(guān)于這個架構(gòu)可以看我這里的項目實例:Twivy。 在Review同事的代碼之后,我才發(fā)現(xiàn)RxJava還能這么玩?各種奇思妙想的作用讓我不得不佩服法國同事的豐富想象力。而這些錯誤使用就像一顆顆定時炸彈一樣埋在代碼里。隨時可能爆炸。 但是反過來一想,并不是所有人都像我一樣喜歡研究RxJava。他們可能僅僅是因為使用了這個架構(gòu)而接觸Rx。而RxJava的掌握并不是一個Android開發(fā)的必要條件。他完全可以一點RxJava也不會也成為一個優(yōu)秀的Android Developer。
RxJava的行為并不可預(yù)期RxJava還有一大毛病就是光看方法名你很難知道他的真正意思。 在初學(xué)RxJava時候,兩個一直糾纏不清的問題就是map和flatMap的區(qū)別。還有flatMap和concatMap的區(qū)別。 簡單的講map是一對一,flatMap是一對N的map然后在進(jìn)行flatten操作。 還有些教程直接寫出flatMap無序,concatMap有序。 其實這些都只是簡單總結(jié),而實際的行為照著相差甚遠(yuǎn)。 比如flatMap在第一個error的時候會不會繼續(xù)繼續(xù)觸發(fā)第二個?如果我想繼續(xù),將如何操作? 再比如concatMap在遇到第一個Observable不會中斷的時候,怎么繼續(xù)下一個? 這些都幾乎是要看源碼或者做多次實驗對比才能得出結(jié)論的問題,而實際工作中并不想去因為這個工具而去浪費太多時間,得不償失。但是如果不做,就像前文提到的定時炸彈一樣。上線直接增加錯誤幾率。
RxJava太容易出錯Uncle Ben 說過:
with great power comes great responsibility. RxJava就是這樣。在簡單易用的同時他太容易被濫用了。我在實際工作中碰到的例子:
val stationId = "5bCP6Iqx"
val statoin:Observable = staionRepo.getStationById(stationId)
val stationLine:Observable = station.flatMap{station ->stationRepo.getLine(station)}
return Observable.merge(station.map{it.toUiModel()},stationLine.map{it.toUiModel()})
乍一看,這幾行代碼并沒有錯。這個Bug還是后臺反饋給我的說為什么android每次都會發(fā)兩個一模一樣的請求? 其實問題就出在stationLine和station并沒有共享結(jié)果。造成了每次請求都要發(fā)兩次。 修改后的代碼:
val stationId = "5bCP6bif"
val statoin:Observable = staionRepo.getStationById(stationId)
return station.publish{selector ->
Observable.merge(selector.map{it.toUiModel()},
selector.flatMap{station -> stationRepo.getLine(station)}
.map{it.toUiModel()})
}
RxJava還是過于理想化了
RxJava承諾出一個完美的異步世界,一切異步操作由上游控制,下游只需要思考如何處理,并不關(guān)心數(shù)據(jù)來源。 而實際過程中,這個過程還是過于理想化了。最直接的例子就是BackPressure的出現(xiàn)。 在數(shù)據(jù)量足夠龐大時,緩存池并不能及時緩存所有生產(chǎn)的數(shù)據(jù),造成越積越多最終OOM。也即是所謂的BackPressure。 再者,函數(shù)式中的Monad來包裹異步這個操作還是過于復(fù)雜了,看過RxJava的朋友都應(yīng)該清楚。某些很簡單的操作符在實現(xiàn)起來其實非常復(fù)雜。追蹤數(shù)據(jù)十分困難,很容易掉入很難Debug的情況。 而且雖然RxJava的文檔是我見過少有寫的非常出色的庫,但是很多操作符如果不讀通源碼,僅僅從Java Doc和Method Signature來觀察,并不清楚期待的行為是什么。就算知道,在一些特殊情況如何處理,仍是一個未知結(jié)果。 同時RxJava雖然解放了上游控制權(quán)力的,也引入了不安全性。如果上游出現(xiàn)了非預(yù)想的問題,下游將很難處理。 其次,RxJava為了這個理想化的世界,引入了太多的overhead。無論是每個操作符都要生成一個新的Observable實例還是蹦床模式的異步解決方案。都生成了太多的Object在堆中存放。這種overhead在輕量級應(yīng)用,或者一些小型異步處理比如數(shù)據(jù)埋點等等行為中,都顯得過于龐大。
RxJava起于異步,卻也不單單是異步Rx在被Erik Meijer 提出的時候,確實是由同步的Iterable推導(dǎo),由主動拉取數(shù)據(jù)改為被動接受數(shù)據(jù)(可參考我之前的文章:一篇不太一樣的RxJava介紹)。 但是在加入函數(shù)是Monad的概念之后,RxJava作為響應(yīng)式數(shù)據(jù)流,應(yīng)用在了更多Callback base的場景中。在Android這種GUI平臺下尤為出色。 多數(shù)基于Redux結(jié)構(gòu)的Android架構(gòu)都或多或少基于RxJava。或者借鑒RxJava的思想。比如Airbnb推出的MvRx。 還有Google在18年io中當(dāng)作Sample App做出的Sunflower,大量使用LiveData。而LiveData無疑也是大量借鑒了RxJava的思想。
總結(jié):RxJava雖然優(yōu)秀,但并不適合所有人即使RxJava有且不僅限于我說的上述幾個問題,但無疑RxJava仍是一個劃時代的優(yōu)秀的異步框架。 但是優(yōu)秀并不代表適合所有人,我在之前推廣RxJava,認(rèn)為這樣的異步基礎(chǔ)應(yīng)該是每一個Android開發(fā)者必不可少的知識點。但實際工作使用兩年之后,我覺得這并不實際,也不必要。RxJava的水平并不能映射一個Android Dev的開發(fā)水平,反之,一個高水平的Android Dev也并不一定對RxJava了解多少。 在這樣的前提下,再加上入門門檻高,易出錯,行為不好預(yù)期等等缺點下。在團(tuán)隊沒有RxJava Expert的情況下我更傾向于直接棄用RxJava,轉(zhuǎn)為更容易使用的異步框架和響應(yīng)式數(shù)據(jù)流。 我在入職新公司的的時候,郵件里寫了這樣一句:
engineering is about trade off
RxJava便是這樣一個庫,甲之蜜糖,乙之砒霜。用的好RxJava,他是一個利器,根本離不開。用不好,他就是你身邊的定時炸彈,隨時爆炸卻又很難拆解。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/7297.html
摘要:觀察者模式面向的需求是對象觀察者對對象被觀察者的某種變化高度敏感,需要在變化的一瞬間做出反應(yīng)。規(guī)定,當(dāng)不會再有新的發(fā)出時,需要觸發(fā)方法作為標(biāo)志。在事件處理過程中出異常時,會被觸發(fā),同時隊列自動終止,不允許再有事件發(fā)出。 我從去年開始使用 RxJava ,到現(xiàn)在一年多了。今年加入了 Flipboard 后,看到 Flipboard 的 Android 項目也在使用 RxJava ,并且使...
摘要:探索專為而設(shè)計的將探討進(jìn)行了何種改進(jìn),以及這些改進(jìn)背后的原因。關(guān)于最友好的文章進(jìn)階前言之前就寫過一篇關(guān)于最友好的文章反響很不錯,由于那篇文章的定位就是簡單友好,因此盡可能的摒棄復(fù)雜的概念,只抓住關(guān)鍵的東西來講,以保證大家都能看懂。 周月切換日歷 一個可以進(jìn)行周月切換的日歷,左右滑動的切換月份,上下滑動可以進(jìn)行周,月不同的視圖切換,可以進(jìn)行事件的標(biāo)記,以及節(jié)假日的顯示,功能豐富 Andr...
摘要:動態(tài)代理個經(jīng)紀(jì)人如何代理個明星掘金在代理模式女朋友這么漂亮,你缺經(jīng)紀(jì)人嗎中我們用寶強(qiáng)的例子介紹了靜態(tài)代理模式的概念。掘金使用從頭創(chuàng)建一個,這種方法比較簡單。 動態(tài)代理:1 個經(jīng)紀(jì)人如何代理 N 個明星 - Android - 掘金在 代理模式:女朋友這么漂亮,你缺經(jīng)紀(jì)人嗎? 中我們用寶強(qiáng)的例子介紹了靜態(tài)代理模式的概念。 本來我的目的是通過大家耳熟能詳?shù)睦觼砑由罾斫猓怯行┚W(wǎng)友指責(zé)...
閱讀 645·2021-10-13 09:39
閱讀 1454·2021-09-09 11:53
閱讀 2646·2019-08-29 13:55
閱讀 724·2019-08-28 18:08
閱讀 2594·2019-08-26 13:54
閱讀 2409·2019-08-26 11:44
閱讀 1837·2019-08-26 11:41
閱讀 3777·2019-08-26 10:15