為什么不使用 RxLifecycle?

本文為翻譯文章淘这,原文連接:為什么不使用RxLifecycle

Why Not RxLifecycle?


Hello. This is Dan Lew. You may or may not know me as the author of?RxLifecycle.

hello,我是Dan Lew檀头,你可能還不知道我就是RxLifecycle.的作者腾供。

Origins

When Trello first started using?RxJava, we were dismayed with how easy it was to leak memory when using it. Seemingly any Subscription you setup would leak unless you explicitly cleared it. As such, we were constantly juggling Subscriptions and unsubscribing when we were done using them.

當(dāng)Trello開始使用RxJava的時候,我們被很容易造成內(nèi)存泄漏的現(xiàn)象嚇到了遮怜。好像你使用的每一個Subscription都會造成內(nèi)存泄漏淋袖,除非你在代碼中明確的去清除它。因此奈泪,我們就在想有么有一種簡單方法來解決這種問題适贸。

Manually handling subscriptions turned out to be rather tedious, so we wanted something that took the thought out of it. For the most part, we simply wanted all our Subscriptionsto end when the Fragment or Activitylifecycle ended. Thus was born RxLifecycle.

手動的處理subscriptions是相當(dāng)無聊的,所以我們想要一種東西來幫我們處理這些問題涝桅。當(dāng)時我們只是想在Fragment或者Activity結(jié)束的時候自動結(jié)束Subscriptions拜姿。所以RxLifecycle誕生了。

With RxLifecycle, you just slap a compose() call onto any stream and it automatically completes the stream when certain lifecycle events happen. It was back to the old days of not having to worry about memory leaks!

使用RxLifecycle冯遂, 你只需要在流的某處使用一個compose()操作符蕊肥。當(dāng)某個lifecycle事件發(fā)生時,那么就會自動的結(jié)束該流蛤肌。那么我們就又可以回到以前不用擔(dān)心內(nèi)存泄漏的美好時光了壁却。

Problems

There have been some lingering problems with RxLifecycle that over time have gnawed at my mind more and more. Roughly in order of importance, here they are:

RxLifecycle存在的一些遺留問題,越來越來使我感到煩惱裸准,根據(jù)重要性排序如下:

Automatic lifecycle detection leads to confusing and sometimes non-deterministic code.

The code is trying to detect where in the lifecycle you are and when to unsubscribe. If you’re subscribing in, say,onStart() then it’s not really a big deal. But if you’re inside some non-Activity component, then you have to give it access to the Activity lifecycle and then hope it is subscribing at the right time in the lifecycle, which is not guaranteed to be the case. Worse still, it is often obscure when subscription go awry.

代碼嘗試去檢查你當(dāng)前處于什么生命周期展东,以及什么時候取消訂閱。也許你會說在OnStart()生命周期里面去處理Observable發(fā)送的items炒俱。但是如果是在一個沒有生命周期的組件里面盐肃,那么就需要該該組件自己去獲取Activity的生命周期,并且希望在正確的時機去subscribing权悟,然而這種行為是沒有保障的砸王。更糟糕的是,當(dāng)訂閱失敗時峦阁,通常是模糊的谦铃。

For example, suppose you’ve got an Adapter that you give an Observableas its data source. It needs to subscribe to the Observable and (at some point later) unsubscribe. The key problem with RxLifecycle here is: how do you know that your automatic unsubscription will happen at the right moment? Inside of the Adapter, there's no way to verify when in the lifecycle you're starting the subscription, and you have even less of a clue of when it's ending. Even if the code works now, if someone moves the Adapter listener code around it could change when it automatically unsubscribes. That’s messy.

例如,假設(shè)你有一個適配器榔昔,傳遞一個Observableas作為該Adapter的數(shù)據(jù)源驹闰。Adapter需要在某個時候訂閱這個Observable和(在某個時間點)取消訂閱。 RxLifecycle的關(guān)鍵問題是:你如何知道自動取消訂閱會在正確的時刻發(fā)生撒会?在適配器的內(nèi)部疮方,沒有辦法去獲取你需要開始訂閱的生命周期,也不知道何時去結(jié)束茧彤。即使代碼現(xiàn)在可以工作,如果有人修改了Adapter的監(jiān)聽器代碼(這里可以理解給Adapter更換了一個新的Observable作為數(shù)據(jù)源)疆栏,可能會導(dǎo)致自動取消訂閱的時機也跟著發(fā)生變動曾掂。這樣子就會很混亂惫谤。

Over time I’ve grown weary of automatic code that sometimes breaks.I much prefer code that is rock-solid and never breaks, even if it means writing more boilerplate.

隨著時間的推移杠河,我已經(jīng)厭倦了自動代碼堂油,有時會破裂。我更喜歡代碼是堅如磐石的久信,永遠(yuǎn)不會中斷许蓖,即使這意味著寫更多的樣板蝴猪。

(Using the more explicit bindUntilEvent() instead of automatic detection somewhat avoids this problem, but lessens the utility of RxLifecycle.)

使用更明確的bindUntilEvent()而不是自動檢測有些避免了這個問題,但是減少了RxLifecycle的實用程序膊爪。

Often times you end up manually handling the Subscription anyways.

Let's extend the Adapter example above. You're listening to one data source, but then whoever is controlling theAdapterwants to send it a new one, so it passes it a newObservable. You want to unsubscribe from the lastObservablebefore subscribing to the new one. None of this has anything to do with the lifecycle, and thus must be handled manually.

一般來說自阱,你最終會通過手動的方式來處理訂閱。繼續(xù)上面的Adapter的示例米酬。你正在監(jiān)聽一個數(shù)據(jù)源沛豌,但這個時候突然有人想要給他一個新的數(shù)據(jù)源,也就是傳遞一個新的Observable給Adapter赃额。您要在訂閱新的Observable之前取消以前訂閱加派。這與生命周期無關(guān),因此必須手動處理跳芳。

Having to manually handle Subscriptions anyways means that RxLifecycle is just an extra headache. It’s confusing to developers - why are we usingunsubscribe()in one place and RxLifecycle in another?

必須手動的去處理訂閱意味著RxLifecycle只是一個額外的麻煩芍锦,它會使開發(fā)者感到迷惑,為什么我在一個地方需要手動處理飞盆,而在另外的地方可以使用RxLifecycle娄琉?

RxLifecycle can only simulate Subscription.unsubscribe().

Because of RxJava 1 limitations, it can (at most) simulate the stream ending due to onComplete(). 99% of the time this is fine, but it leaves open the door for developer mistakes due to subtle differences between onComplete() vs unsubscription.

RxLifecycle只是去模擬取消訂閱。由于RxJava1的限制(RxJava1里面沒有Single/Completable)桨啃,在99%的情況下可以通過OnComplete()來結(jié)束Stream车胡。但是由于onComplete()和untubscription之間的微妙差異,它為開發(fā)人員打開了一扇錯誤的門照瘾。

RxLifecycle throws exceptions for Single/Completable.

Again, because we can only simulate the stream ending.Single/Completable either emit or error, so there’s no other choice. For a while we weren’t using anything except Observable, but now that we’re using other types this can cause problems.

對于Single/Completable匈棘,RxLifecycle會拋出一個意外。同樣的析命,因為我們只能模擬的去結(jié)束流主卫,但是Single/Completable要么發(fā)送數(shù)據(jù)要么發(fā)送錯誤,沒有其他選擇鹃愤。在前面的一段時間內(nèi)我們只能使用Observable(RxJava1的時候)簇搅,但是現(xiàn)在(RxJava2)我們可以使用其他類型的被觀察者,這就會引起一些問題软吐。

Subtle timing bugs require calling RxLifecycle late in the stream.

It’s an avoidable issue, but again can lead to developer mistakes that are best avoided.

由于一些時間上差異瘩将,我們只能在流的末尾去使用RxLifecycle。當(dāng)然這是可以去避免發(fā)生,不過需要開發(fā)者去注意姿现。

RxLint?cannot detect when you’re using RxLifecycle bindings.

RxLint is a handy tool and using RxLifecycle lessens its utility.

在使用了RxLifecycle bingings的時候 RxLint無法檢查肠仪。會降低RxLint的功能。

It generally requires subclassing Activity/Fragment.

While not a requirement (since it’s implemented using interfaces), not subclassing leads to a lot of busywork reproducing what the library does. That’s fine most of the time, but every once in a while we need to use a specializedActivityorFragmentand that causes pain.

大概意思你的Activity/Fragment需要去繼承它要求的基類备典。

(Note that this minor problem can soon be fixed via Google'slifecycle-aware components.)

What it all boils down to is that?the automatic nature of RxLifecycle can have complex, unintended consequences.?While the goal of RxLifecycle was to make life easier, it often ended having the opposite effect.

所有這些都?xì)w結(jié)為RxLifecycle的自動性質(zhì)可能會產(chǎn)生復(fù)雜的异旧,意想不到的后果。雖然RxLifecycle的目標(biāo)是使生活更輕松提佣,但它往往會產(chǎn)生相反的效果

Some of these problems are solved by Uber’s?AutoDispose library, which was born out of years of discussion between?Zac Sweers?and I on how to better write RxLifecycle. In particular, it uses true disposal instead of a simulacrum, does not throw exceptions for Single and Completable, and has fewer restrictions on when it can be used in a stream. I have not, however, just switched to AutoDispose because it doesn't solve all the above problems.

這些問題中的一些由Uber的AutoDispose庫解決吮蛹,這是由ZacSweers和我之間多年來就如何更好地編寫RxLifecycle而進(jìn)行的討論而誕生的。特別地拌屏,它使用真正的處置而不是模擬潮针,不會為Single和Completable拋出異常,并且對于在流中可以使用的限制更少槐壳。然而然低,我沒有轉(zhuǎn)而使用AutoDispose,因為它并不能解決上述所有問題务唐。

Better Patterns

Here’s what I’ve started doing instead of using RxLifecycle.

Manually manageSubscriptions.

That means hanging onto Subscriptions(or stuffing them into a CompositeSubscription) then manually calling unsubscribe()/clear() when appropriate.

意味著保存Subscriptions對象(或著添加到CompositeSubscription對象中)雳攘,然后在適當(dāng)?shù)臅r候手動調(diào)用unsubscribe()/ clear()。

Now that I’m used to the idea it’s not so bad. Its explicit nature makes code easier to reason about. It doesn't require me to think through a complex flow of logic or anticipate unexpected consequences. The extra boilerplate is worth the simplicity.

現(xiàn)在我習(xí)慣了這個想法枫笛,其實它并沒有那么糟糕吨灭。其明確的性質(zhì)使代碼更容易理解。它不需要我思考一個復(fù)雜的邏輯流程或預(yù)期意想不到的后果刑巧。多寫簡單額外的樣板是值得喧兄。

Components pass their Subscriptions upwards until someone handles it.

In other words, if a component is given an Observable from its parent but does not know when to unsubscribe, it passes the resulting Subscription upwards to the parent, since the parent should have a better grasp of the lifecycle.

也就是說,如果一個組件(比如Adapter)從它的父親(比如Activity)獲取到一個Observable啊楚,但是不知道在什么時候去取消訂閱吠冤,那么該組件可以把訂閱這個Observable后返回對象(Subscription往上傳遞給組件的父親(比如Activity),因為父親(Activity)可以更好的去獲得生命周期恭理。

Let’s look at that Adapter example from before. We now provide a function fun listen(data: Observable): Subscription. That way the Adapter can listen to the Observable, but is not responsible for knowing when it needs to stop listening; that responsibility is explicitly given to the owner of the Adapter.

比如上面的Adapter示例拯辙,我們現(xiàn)在提供一個函數(shù)(參數(shù)為Observable類型的data,返回值為Subscription類型)颜价。該函數(shù)讓Adapter可以去監(jiān)聽這個Observable涯保,但是不需要它去處理什么時候停止監(jiān)聽≈苈祝可以由Adapter的擁有者通過返回值Subscription對象控制什么時候取消訂閱夕春。

This pattern can be applied repeatedly to as many layers as you want. You could have an Activity that creates a View that contains a RecyclerView that creates an Adapter that listens to an Observable… but as long as you pass that Subscription upwards at each layer, it will eventually make its way back to a parent (possibly the Activity itself) who knows when to unsubscribe.

該模式可以應(yīng)用到多層的結(jié)構(gòu)(比如Activity-->Fragment--->RecyclerView)。您可以創(chuàng)建一個Activity专挪,該視圖包含一個RecyclerView及志,它創(chuàng)建一個Adapter片排,該Adapter監(jiān)聽了一個Observable。你只要把訂閱Observable后返回的Subscription對面往上層傳遞速侈,它將最終返回到頂層父親(可能是一個Activity)划纽。頂層父親知道什么時候取消訂閱。

Another subtle reason for the switch away from RxLifecycle is our adoption of Kotlin. Kotlin makes manually handling Subscriptions easier for two reasons:

寧外一個不使用RxLifecycle的理由锌畸,如果使用Kotlin,那么手動處理Subscriptions會變得很容易靖避。

Unsubscribing from nullable Subscriptions is a simple one-liner. Before you had to check for nullability (or use a one-liner utility function). Annoying. Now you can just call mySubscription?.unsubscribe().

對一個可null的Subscription調(diào)用Unsubscrib()函數(shù)在Kotlin中只要簡單一行潭枣,比如mySubscription?.unsubscribe()。而不需要像在java中進(jìn)行非空判斷幻捏。

A simple CompositeSubscription operator extension lets you use += to add Subscriptions. Otherwise you need to wrap your whole Observable chain in parentheses, which is a huge pain formatting-wise.

在Kotlin中我們只要使用+= 就可以把Subscriptions添加到CompositeSubscription盆犁。而不是調(diào)用add(subscription)函數(shù)。

Here’s the extension in all its glory:

下面是Kotlin的+=對CompositeSubscription類的擴展函數(shù):

operator fun CompositeSubscription.plusAssign(subscription: Subscription) = add(subscription)

As a result, you can simply use a CompositeSubscription like so:

然后你只需要對CompositeSubscription對象按如下方式簡單調(diào)用+=即可篡九。

compositeSubscription += Observable.just().etc().subscribe()


Mea Culpa

It's not a great feeling when you have built and supported a framework which you no longer believe in. But admitting you were wrong is far more valuable than steadfastly sticking with a subpar solution.

當(dāng)不再相信自己開發(fā)的框架時谐岁,這不是一個很好的感覺,但承認(rèn)你錯了比堅定不移地堅持一個不好的解決方案更有價值榛臼。

So... what now? Well, I'm going to keep maintaining RxLifecycle because people are still using it (including Trello), but in the long term I'm pulling away from it. For those still wanting this sort of library, I would suggest people look into?AutoDispose, since I think it is better architecturally than RxLifecycle.

但是我還是會繼續(xù)維護(hù)RxLifecycle伊佃,因為已經(jīng)有那么多人在使用了(這其中包括Trello),但是在接下來的很長時間后我會漸漸地放棄。如果想使用類似的庫沛善,我建議使用AutoDispose航揉,因為我覺得它的框架比RxLifecycle更優(yōu)秀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末金刁,一起剝皮案震驚了整個濱河市帅涂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尤蛮,老刑警劉巖媳友,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異产捞,居然都是意外死亡醇锚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門轧葛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搂抒,“玉大人,你說我怎么就攤上這事尿扯∏缶В” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵衷笋,是天一觀的道長芳杏。 經(jīng)常有香客問我矩屁,道長,這世上最難降的妖魔是什么爵赵? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任吝秕,我火速辦了婚禮,結(jié)果婚禮上空幻,老公的妹妹穿的比我還像新娘烁峭。我一直安慰自己,他們只是感情好秕铛,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布约郁。 她就那樣靜靜地躺著,像睡著了一般但两。 火紅的嫁衣襯著肌膚如雪鬓梅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天谨湘,我揣著相機與錄音绽快,去河邊找鬼。 笑死紧阔,一個胖子當(dāng)著我的面吹牛坊罢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播寓辱,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼艘绍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了秫筏?” 一聲冷哼從身側(cè)響起诱鞠,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎这敬,沒想到半個月后航夺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡崔涂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年阳掐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冷蚂。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡缭保,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蝙茶,到底是詐尸還是另有隱情艺骂,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布隆夯,位于F島的核電站钳恕,受9級特大地震影響别伏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忧额,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一厘肮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧睦番,春花似錦类茂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至注益,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間溯捆,已是汗流浹背丑搔。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留提揍,地道東北人啤月。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像劳跃,于是被迫代替她去往敵國和親谎仲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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