code小生  一個專注大前端領域的技術平臺

公衆號回覆 Android 加入安卓技術交流羣

作者: W_BinaryTree

juejin.im/post/5cd04b6e51882540e53fdfa2

距離上一次更新也有一段時間了,其實這篇文章我早就想寫,礙於一直沒來得及總結(懶)。所以一直沒有成文。來總結一下我RxJava遇到的坑,或者說我爲什麼不在推薦使用RxJava。相信熟悉或者關注我的朋友,絕大多數都是因爲RxJava。所以看到這個標題你已經會驚訝。作爲RxJava堅定的擁護者,或者說自幹五?爲什麼突然不再支持RxJava了呢?

先講講歷史

在我的文章中已經講過很多次RxJava誕生之初就是因爲異步。再後來借鑑LINQ的思想借用Monad的力量使得 Rx可以使用操作符進行組合將各種複雜的請求簡單化。可以說,RxJava的設計初衷就是圍繞着Asyhconization和Composition。當年的Netflix也是爲了增加服務器的性能和吞吐量來編寫RxJava並開源。才使得RxJava問世。

再聊聊異步

在那個RxJava剛剛火爆的年代,那是一個荒蠻的年代。我們在異步方面資源匱乏,手頭僅有ThreadPool,AsyncTask和Handler這些基礎封裝的異步庫。所以當我們看見RxJava這個新奇的小玩意,當我們看到異步還可以這麼簡單,輕而易舉的解決Concurrency問題。我們當然如獲至寶。

而我們現在選擇就更多了,無論是Java 8本身提供的 CompletableFuture 。還是後起之秀Kotlin上的 Coroutine ,還有Android 上官方提供的 LiveData (這裏說下:雖然本質上線程管理仍需用戶自己,但是常見的比如Room數據庫,Retrofit等等都有現成的LiveDataAdapter,實際上並不需要我們過多操心線程問題)。

相比之下,RxJava優勢並不那麼明顯,相反劣勢卻很突出。

RxJava 門檻太高

相信多數Android開發者並沒有瞭解過或者說深入瞭解過(我自己也沒深入瞭解過)函數式相關的知識。但是如果不瞭解這些,那麼而幾乎可以說不可能融會貫通RxJava的一些概念。舉個例子,一個很著名的Googler:Yigit Boyar。也就是每次IO的那個大鬍子,他的代表作有很多。比如RecyclerView,再比如Architecture Component。這樣一個Android界名人,水平怎麼也有平均以上。但是他在實現LiveData和RxJava適配的時候,同樣出現了由於理解上出的問題,造成錯誤的實現方式。RxJava的門檻過於高,就連我自己推廣這麼久,自己也不敢說對RxJava瞭解有多深刻。經常在常見操作符的使用中出現了或多或少的unexpected behavior。再者,無論國內國外的RxJava教程水平都參差不齊。新手很難鑑別哪些人說的是對的哪些人說的是錯誤的。在這樣魚龍混雜的條件下學好這個高門檻的異步庫更是變得難上加難。很多教程在自己沒有精通的情況下,很容易誤導其他人(包括我自己的文章)。

投入高,收穫少

雖然這點存疑,因爲我自己鑽研RxJava之後確實覺得收穫很大,尤其是經由RxJava窺探了函數式的大門。但是功利的看,RxJava在解決異步處理這個問題上,的確是投入高,收穫少。異步問題是Android開發必不可少的一個環節,可以說掌握異步應該是成爲入門Android開發的敲門磚。而RxJava歸根到底是通過響應式的方式配合Monad來解決異步問題。但是僅僅爲了解決異步問題,學習並精通RxJava並不是必不可少的。相反,精通RxJava需要大量時間和精力,在現在異步編程逐步完善的情況下,完全沒有必要。

你永遠無法預測你同事的RxJava水平

上面幾點可能有點抽象,而這點和接下來的幾點都是我在實際工作中遇到的實際情況。首先就是你並不能預測或者要求你的同事RxJava到達什麼樣的水平。我之前的公司使用了一個簡單的類redux框架。其中RxJava是核心部分,他承載了中間render層和view層的連接。在Review同事的代碼之後,我才發現RxJava還能這麼玩?各種奇思妙想的作用讓我不得不佩服法國同事的豐富想象力。而這些錯誤使用就像一顆顆定時炸彈一樣埋在代碼裏。隨時可能爆炸。但是反過來一想,並不是所有人都像我一樣喜歡研究RxJava。他們可能僅僅是因爲使用了這個架構而接觸Rx。而RxJava的掌握並不是一個Android開發的必要條件。他完全可以一點RxJava也不會也成爲一個優秀的Android Developer。

RxJava的行爲並不可預期

RxJava還 有一大毛病就是光看方法名你很難知道他的真正意思。在初學RxJava時候,兩個一直糾纏不清的問題就是 map flatMap 的區別。還有 flatMap concatMap 的區別。簡單的講 map 是一對一, flatMap 是一對N的map然後在進行 flatten 操作。還有些教程直接寫出 flatMap 無序, concatMap 有序。其實這些都只是簡單總結,而實際的行爲照着相差甚遠。比如 flatMap 在第一個 error 的時候會不會繼續繼續觸發第二個?如果我想繼續,將如何操作?再比如 concatMap 在遇到第一個 Observable 不會中斷的時候,怎麼繼續下一個?這些都幾乎是要看源碼或者做多次實驗對比才能得 出結論的問題,而實際工作中並不想去因爲這個工具而去浪費太多時間,得不償失。但是如果不做,就像前文提到的定時炸彈一樣。上線直接增加錯誤幾率。

RxJava太容易出錯

Uncle Ben 說過:

with great power comes great responsibility. RxJava就是這樣。在簡單易用的同時他太容易被濫用了。我在實際工作中碰到的例子:

val stationId = "5bCP6Iqx"

val statoin:Observable<Station> = staionRepo.getStationById(stationId)

val stationLine:Observable<StationLine> = station.flatMap{station ->stationRepo.getLine(station)}

return Observable.merge(station. map {it.toUiModel()},

stationLine.

map

{it.toUiModel()})

乍一看,這幾行代碼並沒有錯。這個Bug還是後臺反饋給我的說爲什麼android每次都會發兩個一模一樣的請求?其實問題就出在stationLine和station並沒有共享結果。造成了每次請求都要發兩次。修改後的代碼:

val stationId = "5bCP6bif"

val statoin:Observable<Station> = staionRepo.getStationById(stationId)

return station.publish{selector ->

Observable.merge(selector. map {it.toUiModel()},

selector.flatMap{station -> stationRepo.getLine(station)}

. map {it.toUiModel()})

}

RxJava還是過於理想化了

RxJava承諾出一個完美的異步世界,一切異步操作由上游控制,下游只需要思考如何處理,並不關心數據來源。而實際過程中,這個過程還是過於理想化了。最直接的例子就是BackPressure的出現。在數據量足夠龐大時,緩存池並不能及時緩存所有生產的數據,造成越積越多最終OOM。也即是所謂的BackPressure。

再者,函數式中的Monad來包裹異步這個操作還是過於複雜了,看過RxJava的朋友都應該清楚。某些很簡單的操作符在實現起來其實非常複雜。追蹤數據十分困難,很容易掉入很難Debug的情況。而且雖然RxJava的文檔是我見過少有寫的非常出色的庫,但是很多操作符如果不讀通源碼,僅僅從Java Doc和Method Signature來觀察,並不清楚期待的行爲是什麼。就算知道,在一些特殊情況如何處理,仍是一個未知結果。同時RxJava雖然解放了上游控制權力的,也引入了不安全性。如果上游出現了非預想的問題,下游將很難處理。

其次,RxJava爲了這個理想化的世界,引入了太多的overhead。無論是每個操作符都要生成一個新的Observable實例還是蹦牀模式的異步解決方案。都生成了太多的Object在堆中存放。這種overhead在輕量級應用,或者一些小型異步處理比如數據埋點等等行爲中,都顯得過於龐大。

RxJava起於異步,卻也不單單是異步

Rx在被Erik Meijer 提出的時候,確實是由同步的Iterable推導,由主動拉取數據改爲被動接受數據。但是在加入函數是Monad的概念之後,RxJava作爲響應式數據流,應用在了更多Callback base的場景中。在Android這種GUI平臺下尤爲出色。多數基於Redux結構的Android架構都或多或少基於RxJava。或者借鑑RxJava的思想。比如Airbnb推出 的MvRx。還有Google在18年io中當作Sample App做出的Sunflower,大量使用 LiveData LiveData 無疑也是大量 借鑑了RxJava的思想。

總結: RxJava雖然優秀,但並不適合所有人

即使RxJava有且不僅限於我說的上述幾個問題,但無疑RxJava仍是一個劃時代的優秀的異步框架。但是優秀並不代表適合所有人,我在之前推廣RxJava,認爲這樣的異步基礎應該是每一個Android開發者必不可少的知識點。但實際工作使用兩年之後,我覺得這並不實際,也不必要。RxJava的水平並不能映射一個Android Dev的開發水平,反之,一個高水平的Android Dev也並不一定對RxJava瞭解多少。在這樣的前提下,再加上入門門檻高,易出錯,行爲不好預期等等缺點下。在團隊沒有RxJava Expert的情況下我更傾向於直接棄用RxJava,轉爲更容易使用的異步框架和響應式數據流。我在入職新公司的的時候,郵件裏寫了這樣一句:

engineering is about trade off

RxJava便是這樣一個庫,甲之蜜糖,乙之砒霜。用的好RxJava,他是一個利器,根本離不開。用不好,他就是你身邊的定時炸彈,隨時爆炸卻又很難拆解。

相關閱讀

大佬們,一波 RxJava 3.0 來襲,請做好準備~

Kotlin + Mvp + RxJava + Retrofit 心得體會

Android 讓你的 Room 搭上 RxJava 的順風車 從重複的代碼中解脫出來

如果你有寫博客的好習慣

歡迎投稿

點個在看,小生感恩 :heart:

相關文章