本文為翻譯文章,原文連接:為什么不使用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開(kāi)始使用RxJava的時(shí)候,我們被很容易造成內(nèi)存泄漏的現(xiàn)象嚇到了。好像你使用的每一個(gè)Subscription都會(huì)造成內(nèi)存泄漏,除非你在代碼中明確的去清除它。因此,我們就在想有么有一種簡(jiǎn)單方法來(lái)解決這種問(wèn)題。
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.
手動(dòng)的處理subscriptions是相當(dāng)無(wú)聊的,所以我們想要一種東西來(lái)幫我們處理這些問(wèn)題。當(dāng)時(shí)我們只是想在Fragment或者Activity結(jié)束的時(shí)候自動(dòng)結(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, 你只需要在流的某處使用一個(gè)compose()操作符。當(dāng)某個(gè)lifecycle事件發(fā)生時(shí),那么就會(huì)自動(dòng)的結(jié)束該流。那么我們就又可以回到以前不用擔(dān)心內(nèi)存泄漏的美好時(shí)光了。
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存在的一些遺留問(wèn)題,越來(lái)越來(lái)使我感到煩惱,根據(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)前處于什么生命周期,以及什么時(shí)候取消訂閱。也許你會(huì)說(shuō)在OnStart()生命周期里面去處理Observable發(fā)送的items。但是如果是在一個(gè)沒(méi)有生命周期的組件里面,那么就需要該該組件自己去獲取Activity的生命周期,并且希望在正確的時(shí)機(jī)去subscribing,然而這種行為是沒(méi)有保障的。更糟糕的是,當(dāng)訂閱失敗時(shí),通常是模糊的。
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è)你有一個(gè)適配器,傳遞一個(gè)Observableas作為該Adapter的數(shù)據(jù)源。Adapter需要在某個(gè)時(shí)候訂閱這個(gè)Observable和(在某個(gè)時(shí)間點(diǎn))取消訂閱。 RxLifecycle的關(guān)鍵問(wèn)題是:你如何知道自動(dòng)取消訂閱會(huì)在正確的時(shí)刻發(fā)生?在適配器的內(nèi)部,沒(méi)有辦法去獲取你需要開(kāi)始訂閱的生命周期,也不知道何時(shí)去結(jié)束。即使代碼現(xiàn)在可以工作,如果有人修改了Adapter的監(jiān)聽(tīng)器代碼(這里可以理解給Adapter更換了一個(gè)新的Observable作為數(shù)據(jù)源),可能會(huì)導(dǎo)致自動(dòng)取消訂閱的時(shí)機(jī)也跟著發(fā)生變動(dòng)。這樣子就會(huì)很混亂。
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.
隨著時(shí)間的推移,我已經(jīng)厭倦了自動(dòng)代碼,有時(shí)會(huì)破裂。我更喜歡代碼是堅(jiān)如磐石的,永遠(yuǎn)不會(huì)中斷,即使這意味著寫(xiě)更多的樣板。
(Using the more explicit bindUntilEvent() instead of automatic detection somewhat avoids this problem, but lessens the utility of RxLifecycle.)
使用更明確的bindUntilEvent()而不是自動(dòng)檢測(cè)有些避免了這個(gè)問(wèn)題,但是減少了RxLifecycle的實(shí)用程序。
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.
一般來(lái)說(shuō),你最終會(huì)通過(guò)手動(dòng)的方式來(lái)處理訂閱。繼續(xù)上面的Adapter的示例。你正在監(jiān)聽(tīng)一個(gè)數(shù)據(jù)源,但這個(gè)時(shí)候突然有人想要給他一個(gè)新的數(shù)據(jù)源,也就是傳遞一個(gè)新的Observable給Adapter。您要在訂閱新的Observable之前取消以前訂閱。這與生命周期無(wú)關(guān),因此必須手動(dòng)處理。
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?
必須手動(dòng)的去處理訂閱意味著RxLifecycle只是一個(gè)額外的麻煩,它會(huì)使開(kāi)發(fā)者感到迷惑,為什么我在一個(gè)地方需要手動(dòng)處理,而在另外的地方可以使用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里面沒(méi)有Single/Completable),在99%的情況下可以通過(guò)OnComplete()來(lái)結(jié)束Stream。但是由于onComplete()和untubscription之間的微妙差異,它為開(kāi)發(fā)人員打開(kāi)了一扇錯(cuò)誤的門(mén)。
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.
對(duì)于Single/Completable,RxLifecycle會(huì)拋出一個(gè)意外。同樣的,因?yàn)槲覀冎荒苣M的去結(jié)束流,但是Single/Completable要么發(fā)送數(shù)據(jù)要么發(fā)送錯(cuò)誤,沒(méi)有其他選擇。在前面的一段時(shí)間內(nèi)我們只能使用Observable(RxJava1的時(shí)候),但是現(xiàn)在(RxJava2)我們可以使用其他類型的被觀察者,這就會(huì)引起一些問(wèn)題。
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.
由于一些時(shí)間上差異,我們只能在流的末尾去使用RxLifecycle。當(dāng)然這是可以去避免發(fā)生,不過(guò)需要開(kāi)發(fā)者去注意。
RxLint?cannot detect when you’re using RxLifecycle bindings.
RxLint is a handy tool and using RxLifecycle lessens its utility.
在使用了RxLifecycle bingings的時(shí)候 RxLint無(wú)法檢查。會(huì)降低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的自動(dòng)性質(zhì)可能會(huì)產(chǎn)生復(fù)雜的,意想不到的后果。雖然RxLifecycle的目標(biāo)是使生活更輕松,但它往往會(huì)產(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.
這些問(wèn)題中的一些由Uber的AutoDispose庫(kù)解決,這是由ZacSweers和我之間多年來(lái)就如何更好地編寫(xiě)RxLifecycle而進(jìn)行的討論而誕生的。特別地,它使用真正的處置而不是模擬,不會(huì)為Single和Completable拋出異常,并且對(duì)于在流中可以使用的限制更少。然而,我沒(méi)有轉(zhuǎn)而使用AutoDispose,因?yàn)樗⒉荒芙鉀Q上述所有問(wèn)題。
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對(duì)象(或著添加到CompositeSubscription對(duì)象中),然后在適當(dāng)?shù)臅r(shí)候手動(dòng)調(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í)慣了這個(gè)想法,其實(shí)它并沒(méi)有那么糟糕。其明確的性質(zhì)使代碼更容易理解。它不需要我思考一個(gè)復(fù)雜的邏輯流程或預(yù)期意想不到的后果。多寫(xiě)簡(jiǎn)單額外的樣板是值得。
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.
也就是說(shuō),如果一個(gè)組件(比如Adapter)從它的父親(比如Activity)獲取到一個(gè)Observable,但是不知道在什么時(shí)候去取消訂閱,那么該組件可以把訂閱這個(gè)Observable后返回對(duì)象(Subscription往上傳遞給組件的父親(比如Activity),因?yàn)楦赣H(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)在提供一個(gè)函數(shù)(參數(shù)為Observable類型的data,返回值為Subscription類型)。該函數(shù)讓Adapter可以去監(jiān)聽(tīng)這個(gè)Observable,但是不需要它去處理什么時(shí)候停止監(jiān)聽(tīng)??梢杂葾dapter的擁有者通過(guò)返回值Subscription對(duì)象控制什么時(shí)候取消訂閱。
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)建一個(gè)Activity,該視圖包含一個(gè)RecyclerView,它創(chuàng)建一個(gè)Adapter,該Adapter監(jiān)聽(tīng)了一個(gè)Observable。你只要把訂閱Observable后返回的Subscription對(duì)面往上層傳遞,它將最終返回到頂層父親(可能是一個(gè)Activity)。頂層父親知道什么時(shí)候取消訂閱。
Another subtle reason for the switch away from RxLifecycle is our adoption of Kotlin. Kotlin makes manually handling Subscriptions easier for two reasons:
寧外一個(gè)不使用RxLifecycle的理由,如果使用Kotlin,那么手動(dòng)處理Subscriptions會(huì)變得很容易。
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().
對(duì)一個(gè)可null的Subscription調(diào)用Unsubscrib()函數(shù)在Kotlin中只要簡(jiǎn)單一行,比如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的+=對(duì)CompositeSubscription類的擴(kuò)展函數(shù):
operator fun CompositeSubscription.plusAssign(subscription: Subscription) = add(subscription)
As a result, you can simply use a CompositeSubscription like so:
然后你只需要對(duì)CompositeSubscription對(duì)象按如下方式簡(jiǎn)單調(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)不再相信自己開(kāi)發(fā)的框架時(shí),這不是一個(gè)很好的感覺(jué),但承認(rèn)你錯(cuò)了比堅(jiān)定不移地堅(jiān)持一個(gè)不好的解決方案更有價(jià)值。
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.
但是我還是會(huì)繼續(xù)維護(hù)RxLifecycle,因?yàn)橐呀?jīng)有那么多人在使用了(這其中包括Trello),但是在接下來(lái)的很長(zhǎng)時(shí)間后我會(huì)漸漸地放棄。如果想使用類似的庫(kù),我建議使用AutoDispose,因?yàn)槲矣X(jué)得它的框架比RxLifecycle更優(yōu)秀。