距離上一次更新也有一段時間了,其實這篇文章我早就想寫,礙於一直沒來得及總結(懶)。因此一直沒有成文。來總結一下我RxJava遇到的坑,或者說我爲何不在推薦使用RxJava。 相信熟悉或者關注個人朋友,絕大多數都是由於RxJava。因此看到這個標題你已經會驚訝。 做爲RxJava堅決的擁護者,或者說自幹五?爲何忽然再也不支持RxJava了呢?java
在個人文章中已經講過不少次RxJava誕生之初就是由於異步。再後來借鑑LINQ的思想借用Monad的力量使得 Rx可使用操做符進行組合將各類複雜的請求簡單化。 能夠說,RxJava的設計初衷就是圍繞着Asyhconization和Composition。當年的Netflix也是爲了增長服務器的性能和吞吐量來編寫RxJava並開源。才使得RxJava問世。詳細關於這段能夠參考個人知乎回答:你會在實際工做中使用 rxjava 嗎?android
在那個RxJava剛剛火爆的年代,那是一個荒蠻的年代。咱們在異步方面資源匱乏,手頭僅有ThreadPool,AsyncTask和Handler這些基礎封裝的異步庫。因此當咱們看見RxJava這個新奇的小玩意,當咱們看到異步還能夠這麼簡單,垂手可得的解決Concurrency問題。咱們固然如獲至寶。 而咱們如今選擇就更多了,不管是Java 8自己提供的CompletableFuture
。仍是後起之秀Kotlin上的Coroutine
,還有Android 上官方提供的LiveData
(這裏說下: 雖然本質上線程管理仍需用戶本身,可是常見的好比Room數據庫,Retrofit等等都有現成的LiveDataAdapter,實際上並不須要咱們過多操心線程問題)。 相比之下,RxJava優點並不那麼明顯,相反劣勢卻很突出。git
相信多數Android開發者並無瞭解過或者說深刻了解過(我本身也沒深刻了解過)函數式相關的知識。可是若是不瞭解這些,那麼而幾乎能夠說不可能融會貫通RxJava的一些概念。 舉個例子,一個很著名的Googler:Yigit Boyar。也就是每次IO的那個大鬍子,他的表明做有不少。好比RecyclerView,再好比Architecture Component。這樣一個Android界名人,水平怎麼也有平均以上。可是他在實現LiveData和RxJava適配的時候,一樣出現了因爲理解上出的問題,形成錯誤的實現方式。 RxJava的門檻過於高,就連我本身推廣這麼久,本身也不敢說對RxJava瞭解有多深入。常常在常見操做符的使用中出現了或多或少的unexpected behavior。 再者,不管國內國外的RxJava教程水平都良莠不齊。新手很難鑑別哪些人說的是對的哪些人說的是錯誤的。在這樣魚龍混雜的條件下學好這個高門檻的異步庫更是變得難上加難。不少教程在本身沒有精通的狀況下,很容易誤導其餘人(包括我本身的文章)。github
雖然這點存疑,由於我本身鑽研RxJava以後確實以爲收穫很大,尤爲是經由RxJava窺探了函數式的大門。可是功利的看,RxJava在解決異步處理這個問題上,的確是投入高,收穫少。 異步問題是Android開發必不可少的一個環節,能夠說掌握異步應該是成爲入門Android開發的敲門磚。而RxJava歸根究竟是經過響應式的方式配合Monad來解決異步問題。可是僅僅爲了解決異步問題,學習並精通RxJava並非必不可少的。相反,精通RxJava須要大量時間和精力,在如今異步編程逐步完善的狀況下,徹底沒有必要。數據庫
上面幾點可能有點抽象,而這點和接下來的幾點都是我在實際工做中遇到的實際狀況。首先就是你並不能預測或者要求你的同事RxJava到達什麼樣的水平。 我以前的公司使用了一個簡單的類redux框架。其中RxJava是核心部分,他承載了中間render層和view層的鏈接。具體關於這個架構能夠看我這裏的項目實例:Twivy。 在Review同事的代碼以後,我才發現RxJava還能這麼玩?各類奇思妙想的做用讓我不得不佩服法國同事的豐富想象力。而這些錯誤使用就像一顆顆定時炸彈同樣埋在代碼裏。隨時可能爆炸。 可是反過來一想,並非全部人都像我同樣喜歡研究RxJava。他們可能僅僅是由於使用了這個架構而接觸Rx。而RxJava的掌握並非一個Android開發的必要條件。他徹底能夠一點RxJava也不會也成爲一個優秀的Android Developer。編程
RxJava還有一大毛病就是光看方法名你很難知道他的真正意思。 在初學RxJava時候,兩個一直糾纏不清的問題就是map
和flatMap
的區別。還有flatMap
和concatMap
的區別。 簡單的講map
是一對一,flatMap
是一對N的map而後在進行flatten
操做。 還有些教程直接寫出flatMap
無序,concatMap
有序。 其實這些都只是簡單總結,而實際的行爲照着相差甚遠。 好比flatMap
在第一個error的時候會不會繼續繼續觸發第二個?若是我想繼續,將如何操做? 再好比concatMap
在遇到第一個Observable
不會中斷的時候,怎麼繼續下一個? 這些都幾乎是要看源碼或者作屢次實驗對比才能得出結論的問題,而實際工做中並不想去由於這個工具而去浪費太多時間,得不償失。可是若是不作,就像前文提到的定時炸彈同樣。上線直接增長錯誤概率。redux
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承諾出一個完美的異步世界,一切異步操做由上游控制,下游只須要思考如何處理,並不關心數據來源。 而實際過程當中,這個過程仍是過於理想化了。最直接的例子就是BackPressure的出現。 在數據量足夠龐大時,緩存池並不能及時緩存全部生產的數據,形成越積越多最終OOM。也便是所謂的BackPressure。 再者,函數式中的Monad來包裹異步這個操做仍是過於複雜了,看過RxJava的朋友都應該清楚。某些很簡單的操做符在實現起來其實很是複雜。追蹤數據十分困難,很容易掉入很難Debug的狀況。 並且雖然RxJava的文檔是我見過少有寫的很是出色的庫,可是不少操做符若是不讀通源碼,僅僅從Java Doc和Method Signature來觀察,並不清楚期待的行爲是什麼。就算知道,在一些特殊狀況如何處理,還是一個未知結果。 同時RxJava雖然解放了上游控制權力的,也引入了不安全性。若是上游出現了非預想的問題,下游將很難處理。 其次,RxJava爲了這個理想化的世界,引入了太多的overhead。不管是每一個操做符都要生成一個新的Observable實例仍是蹦牀模式的異步解決方案。都生成了太多的Object在堆中存放。這種overhead在輕量級應用,或者一些小型異步處理好比數據埋點等等行爲中,都顯得過於龐大。
Rx在被Erik Meijer 提出的時候,確實是由同步的Iterable推導,由主動拉取數據改成被動接受數據(可參考我以前的文章:一篇不太同樣的RxJava介紹)。 可是在加入函數是Monad的概念以後,RxJava做爲響應式數據流,應用在了更多Callback base的場景中。在Android這種GUI平臺下尤其出色。 多數基於Redux結構的Android架構都或多或少基於RxJava。或者借鑑RxJava的思想。好比Airbnb推出的MvRx。 還有Google在18年io中看成Sample App作出的Sunflower,大量使用LiveData
。而LiveData
無疑也是大量借鑑了RxJava的思想。
即便RxJava有且不只限於我說的上述幾個問題,但無疑RxJava還是一個劃時代的優秀的異步框架。 可是優秀並不表明適合全部人,我在以前推廣RxJava,認爲這樣的異步基礎應該是每個Android開發者必不可少的知識點。但實際工做使用兩年以後,我以爲這並不實際,也沒必要要。RxJava的水平並不能映射一個Android Dev的開發水平,反之,一個高水平的Android Dev也並不必定對RxJava瞭解多少。 在這樣的前提下,再加上入門門檻高,易出錯,行爲很差預期等等缺點下。在團隊沒有RxJava Expert的狀況下我更傾向於直接棄用RxJava,轉爲更容易使用的異步框架和響應式數據流。 我在入職新公司的的時候,郵件裏寫了這樣一句:
engineering is about trade off
RxJava即是這樣一個庫,甲之蜜糖,乙之砒霜。用的好RxJava,他是一個利器,根本離不開。用很差,他就是你身邊的定時炸彈,隨時爆炸卻又很難拆解。