本文已受權「郭霖」微信公衆號獨家原創發佈java
熟悉RxJava的同窗,當咱們開啓一個異步任務時,一般須要在Activity/Fragment銷燬時,及時關閉異步任務,不然就會有內存泄漏的微信。git
通常的作法是訂閱成功後,拿到Disposable
對象,在Activity/Fragment銷燬時,調用Disposable
對象的dispose()
方法,將異步任務中斷,也就是中斷RxJava的管道,代碼以下:github
Disposable disposable = Observable
.interval(0, 1, TimeUnit.SECONDS) //開啓一個定時器
.subscribe(aLong -> {
});
//Activity/Fragment銷燬時,中斷RxJava管道
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
複製代碼
這種作法在代碼的執行效率上是最高效、性能最優的,然而這種作法在開發效率上倒是最低的安全
試想,若是咱們開啓了n個異步任務,就須要在Activity/Fragment銷燬時中斷n個異步任務。對於這種寫法,身患強迫症的我,實在不能接受。也許大家會說,可使用CompositeDisposable
類,就能夠避免手寫關閉n個異步任務的代碼,只須要關閉一次便可。沒毛病,確實能夠,然而這種作法也僅僅是避免了咱們手寫關閉異步任務的代碼而已。追求極致的我,也不能接受這種寫法,此時我就想,能不能就用一行代碼解決這個問題呢?因而乎,就開啓了個人探索之路,因而乎,就有了RxLife。微信
先來介紹下RxLife,相較於trello/RxLifecycle、uber/AutoDispose,具備以下優點:異步
gradle依賴ide
dependencies {
implementation 'com.rxjava.rxlife:rxlife:1.0.8'
//if you use AndroidX
implementation 'com.rxjava.rxlife:rxlife-x:1.0.8'
}
複製代碼
首先,咱們來看看在Activity/Fragment上如何使用,以下:post
//在Activity/Fragment上
Observable.interval(1, 1, TimeUnit.SECONDS)
.as(RxLife.as(this)) //這裏this 爲LifecycleOwner接口對象
.subscribe(aLong -> {
Log.e("LJX", "onNext aLong=" + aLong);
});
複製代碼
沒錯,就是這麼簡單粗暴,在這,咱們只須要將RxLife.as(this)
傳入RxJava的as
操做符便可。此時當Activity/Fragment銷燬,就會自動關閉RxJava管道,避免內存泄漏。性能
接着來看看在View上如何使用,以下:學習
//在View上
Observable.interval(1, 1, TimeUnit.SECONDS) //隔一秒發送一條消息
.as(RxLife.as(this)) //這裏this 爲View對象
.subscribe(aLong -> {
Log.e("LJX", "onNext aLong=" + aLong);
});
複製代碼
疑?這跟上面的代碼不是如出一轍的嗎?是的,代碼如出一轍,可是在這咱們傳入的this
是一個View對象。此時當View從窗口中移除時(執行了onDetachedFromWindow
方法),就會自動關閉RxJava管道,避免內存泄漏。
ViewModel是Google Jetpack裏面的組件之一,因爲它能自動感知Activity/Fragmeng的銷燬,因此RxLife單獨爲它作了適配。在ViewModel中使用RxLife,須要繼承RxLife的 ScopeViewModel 類,而後就能夠跟上面同樣,優雅的使用RxLife.as(this)
,以下:
public class MyViewModel extends ScopeViewModel {
public MyViewModel() {
Observable.interval(1, 1, TimeUnit.SECONDS)
.as(RxLife.as(this)) //這裏的this 爲Scope接口對象
.subscribe(aLong -> {
Log.e("LJX", "onNext aLong=" + aLong);
});
}
}
複製代碼
此時當Activity/Fragmeng銷燬時,就會自動關閉RxJava管道,避免內存泄漏。
注意:要想ViewModel對象感知Activity/Fragment銷燬事件,就不能使用new 關鍵字建立對象,必需要經過ViewModelProviders類獲取ViewModel對象,以下:
//在Activity/Fragment上
MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class)
複製代碼
相信你們對MVP都很是的熟悉了,在P層,咱們通常都有發送Http請求的需求, 此時,咱們也但願,在Activity/Fragment銷燬時,能自動將Http關閉,因此RxLife對任意類作了點適配工做。在任意類中,咱們須要繼承RxLife的BaseScope類,而後就優雅的使用RxLife.as(this)
了,以下:
public class Presenter extends BaseScope {
public Presenter(LifecycleOwner owner) {
super(owner);
Observable.interval(1, 1, TimeUnit.SECONDS)
.as(RxLife.as(this)) //這裏的this 爲Scope接口對象
.subscribe(aLong -> {
Log.e("LJX", "onNext aLong=" + aLong);
});
}
}
複製代碼
在上面的代碼中,咱們使用了as
操做符,而後在kotlin中,as是一個關鍵字,使用起來就不是很方便,因此RxLife對kotlin作了適配工做,在kotlin中,咱們可使用life
替代as
操做符,而且更加的簡潔,以下:
Observable.intervalRange(1, 100, 0, 200, TimeUnit.MILLISECONDS)
.life(this)
.subscribe { aLong ->
Log.e("LJX", "onNext aLong=" + aLong)
}
複製代碼
提及原理,其實trello/RxLifecycle、uber/AutoDispose、RxLife三者的原理都是同樣的,都是拿到最低層觀察者的Disposable
對象,而後在某個時機,調用該對象的Disposable.dispose()
方法中斷管道,以達到目的。 原理都同樣,然而實現卻大不相同,
trello/RxLifecycle (3.0.0版本) 內部只有一個管道,但卻有兩個事件源,一個發送生命週期狀態變化,一個發送正常業務邏輯,最終經過takeUntil操做符對事件進行過濾,當監聽到符合條件的事件時,就會將管道中斷,從而到達目的
uber/AutoDispose(1.2.0版本) 內部維護了兩個管道,一個是發送生命週期狀態變化的管道,咱們稱之爲A管道,另外一個是業務邏輯的管道,咱們稱至爲B管道,B管道持有A管道的觀察者引用,故能監聽A管道的事件,當監聽到符合條件的事件時,就會將A、B管道同時中斷,從而到達目的
RxLife 內部只有一個業務邏輯的管道,經過自定義觀察者,拿到Disposable對象,暴露給Scope接口,Scope的實現者就能夠在合適的時機調用Disposable.dispose()
方法中斷管道,從而到達目的
光從文字層面上所原理,好像有點抽象,接下來,咱們看看RxLife在代碼層面上是如何實現的。在上面的代碼案例中,咱們皆能看到RxLife.as(this)
這行代碼的身影,那這個as
方法接收的是什麼類型的參數呢?咱們看看源碼:
上面一共有10個as
系列方法,其中有8個是對外提供的。並且前面9個方法最終都會調用第10個as(Scope scope, boolean onMain)
方法。
咱們先粗略來看幾個方法,
as(LifecycleOwnerowner owner) 方法,接收的是一個LifecycleOwner
接口對象,簡單介紹下這個接口,這個接口對象能使咱們自定義的類感知Activity/Fragment的生命週期回調。咱們常見的Activity/Fragment就實現了這個接口,因此咱們就可以在Activity/Fragment中調用此as方法
as(View view) 這個方法就很直觀了,直接接收一個View對象,咱們在View上調用的就是這個方法
as(Scope scope) 方法接收一個Scope接口對象,後面會對這個接口介紹,這裏能夠告訴你們的是,在上面的ViewModel及任意類中繼承的ScopeViewModel、BaseScope類都實現了Scope接口,因此咱們在ViewModel及任意類中調用的就是這個as方法
Scope,翻譯過來就是做用域的意思。那麼什麼是做用域,簡單來講,就是一個對象從建立到死亡,這就是它的做用域,好比:Activity/Fragment的做用域就是從onCreate
到onDestroy
;View的做用域就是從onAttachedToWindow
到onDetachedFromWindow
;ViewModel的做用域就是從構造方法
到onCleared
方法;其它任意類的做用域就是從建立到銷燬,固然,你也能夠本身指定一些類的做用域。到這,咱們來看看Scope接口裏面都有啥:
public interface Scope {
//訂閱事件時,回調本方法,即在onSubscribe(Disposable d)方法執行時回調本方法
void onScopeStart(Disposable d);
//onError/onComplete 時調回調此方法,即事件正常結束時回調
void onScopeEnd();
}
複製代碼
此接口描述的就是RxJava的做用域,即從事件訂閱到事件結束。到這,也許有人已經知道了,只要咱們實現了這個接口,就能拿到Disposable
對象,而後就能夠在某個時刻,中斷RxJava短道,提早結束RxJava做用域。從而使得RxJava的做用域小於等於調用者的做用域,避免了內存泄漏。
咱們簡單看一下BaseScope
類的具體實現
public class BaseScope implements Scope, GenericLifecycleObserver {
private CompositeDisposable mDisposables;
public BaseScope(LifecycleOwner owner) {
owner.getLifecycle().addObserver(this);
}
@Override
public void onScopeStart(Disposable d) {
addDisposable(d);
}
@Override
public void onScopeEnd() {}
private void addDisposable(Disposable disposable) {
CompositeDisposable disposables = mDisposables;
if (disposables == null) {
disposables = mDisposables = new CompositeDisposable();
}
disposables.add(disposable);
}
private void dispose() {
final CompositeDisposable disposables = mDisposables;
if (disposables == null) return;
disposables.dispose();
}
@Override
public void onStateChanged(LifecycleOwner source, Event event) {
//Activity/Fragment 生命週期回調
if (event == Event.ON_DESTROY) { //Activity/Fragment 銷燬
source.getLifecycle().removeObserver(this);
dispose(); //中斷RxJava管道
}
}
}
複製代碼
能夠看到,BaseScope實現很是簡單,在onScopeStart
方法中拿到Disposable
對象添加進CompositeDisposable
對象,而後在Activity/Fragment銷燬使,調用CompositeDisposable對象的dispose
方法,統一中斷RxJava管道,從而達到目的。
Scope一共有4個實現類,分別是:LifecycleScope
、ViewScope
、ScopeViewModel
及BaseScope
,BaseScope上面已經介紹,其它3個原理都同樣,只是在實現上會有一點點不一樣,這就不在一一介紹了。
咱們知道,任意類想要監聽Activity/Fragment生命週期回調,都必需要實現LifecycleObserver
接口,而後經過如下代碼添加進觀察者隊列
owner.getLifecycle().addObserver(this);
複製代碼
這行代碼的內部是經過FastSafeIterableMap
類來管理觀察者的,而這個類是非線程安全的,以下:
trello/RxLifecycle RxLifecycle庫是AndroidLifecycle
類感知生命週期,簡單看看源碼:
public final class AndroidLifecycle implements LifecycleProvider<Lifecycle.Event>, LifecycleObserver {
public static LifecycleProvider<Lifecycle.Event> createLifecycleProvider(LifecycleOwner owner) {
return new AndroidLifecycle(owner);
}
private final BehaviorSubject<Lifecycle.Event> lifecycleSubject = BehaviorSubject.create();
private AndroidLifecycle(LifecycleOwner owner) {
owner.getLifecycle().addObserver(this);
}
//中間省略部分代碼
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
void onEvent(LifecycleOwner owner, Lifecycle.Event event) {
lifecycleSubject.onNext(event);
if (event == Lifecycle.Event.ON_DESTROY) {
owner.getLifecycle().removeObserver(this);
}
}
}
複製代碼
能夠看到,RxLifecycle是在對象建立時添加觀察者,且它沒有作任何處理,若是你在子線程使用,就須要額外注意了,並且它只有在頁面銷燬時,纔會移除觀察者,試想,咱們在首頁通常都會有很是多的請求,而這每個請求都會有一個AndroidLifecycle對象,咱們想請求結束就要回收這個對象,然而,這個對象仍是觀察者隊列裏,就致使了沒辦法回收,若是咱們不停下拉刷新、上拉加載更多,對內存就是一個挑戰。
RxLifecycle還有一個弊端時,當Activity/Fragment銷燬時,始終會往下游發送一個onComplete事件,這對於在onComplete事件中有業務邏輯的同窗來講,無疑是致命的打擊。
uber/AutoDispose AutoDispose庫咱們看LifecycleEventsObservable類,以下
class LifecycleEventsObservable extends Observable<Event> {
//省略部分代碼
@Override protected void subscribeActual(Observer<? super Event> observer) {
ArchLifecycleObserver archObserver = new ArchLifecycleObserver(lifecycle, observer, eventsObservable);
observer.onSubscribe(archObserver);
if (!isMainThread()) { //非主線程,直接拋出異常
observer.onError(new IllegalStateException("Lifecycles can only be bound to on the main thread!"));
return;
}
lifecycle.addObserver(archObserver); //添加觀察者
if (archObserver.isDisposed()) {
lifecycle.removeObserver(archObserver);
}
}
//省略部分代碼
複製代碼
能夠看到,AutoDispose是在事件訂閱時添加觀察者,而且當前非主線程時,直接拋出異常,也就說明使用AutoDispose不能在子線程訂閱事件。在移除觀察者方面,AutoDispose會在事件結束或者頁面銷燬時移除觀察者,這一點要優於RxLifecycle。
RxLife
RxLife庫咱們看AbstractLifecycle類,以下:
public abstract class AbstractLifecycle<T> extends AtomicReference<T> implements Disposable {
//省略部分代碼
protected final void addObserver() throws Exception {
//Lifecycle添加監聽器須要在主線程執行
if (isMainThread() || !(scope instanceof LifecycleScope)) {
addObserverOnMain();
} else {
final Object object = mObject;
AndroidSchedulers.mainThread().scheduleDirect(() -> {
addObserverOnMain();
synchronized (object) {
isAddObserver = true;
object.notifyAll(); //喚醒等待的線程
}
});
synchronized (object) { //加鎖等待
while (!isAddObserver) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//省略部分代碼
}
複製代碼
能夠看到,RxLife對子線程作了額外的操做,在子線程經過同步鎖,添加完觀察者後再往下走,且RxLife一樣會在事件結束或者頁面銷燬時移除觀察者。
咱們知道對View添加OnAttachStateChangeListener監聽器是線程安全的,以下:
那爲什麼AutoDispose庫中的DetachEventCompletable依然會線程作判斷?代碼以下 請大神爲我解答。RxLife類裏面的as系列方法,皆適用於Observable、Flowable、ParallelFlowable、Single、Maybe、Completable這6個被觀察者對象,道理都同樣,這裏不在一一講解。 另外,在Activity/Fragment上,若是你想在某個生命週期方法中斷管道,可以使用as
操做符的重載方法,以下:
//在Activity/Fragment上
Observable.interval(1, 1, TimeUnit.SECONDS) //隔一秒發送一條消息
.as(RxLife.as(this, Event.ON_STOP)) //在onStop方法中斷管道
.subscribe(aLong -> {
Log.e("LJX", "accept=" + aLong);
});
複製代碼
此時若是你還想在主線程回調觀察者,使用asOnMain
方法便可,以下:
//在Activity/Fragment上
Observable.interval(1, 1, TimeUnit.SECONDS) //隔一秒發送一條消息
.as(RxLife.asOnMain(this, Event.ON_STOP)) //在onStop方法中斷管道,並在主線程回調觀察者
.subscribe(aLong -> {
Log.e("LJX", "accept=" + aLong);
});
//等同於
Observable.interval(1, 1, TimeUnit.SECONDS) //隔一秒發送一條消息
.observeOn(AndroidSchedulers.mainThread())
.as(RxLife.as(this, Event.ON_STOP)) //在onStop方法中斷管道,並在主線程回調觀察者
.subscribe(aLong -> {
Log.e("LJX", "accept=" + aLong);
});
複製代碼
在Activity/Fragment/View中,無需作任何準備工做就能夠直接使用RxLife.as(this)
, 然而在ViewModel及任意類,須要分別繼承ScopeViewModel
及BaseScope
類纔可使用RxLife.as(this)
,這多少都帶有點侵入性,但這也是沒有辦法的辦法,若是你以爲這樣不能接受,RxLife容許你自行去實現Scope接口。
注:必定要使用ViewModelProviders獲取ViewModel對象,以下
//在Activity/Fragment上
MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class)
複製代碼
本人水平有限,如文章中有看法不到之處,請廣大讀者指正,RxLife剛出來不久,使用過程當中若有遇到問題,請在github上留言。
歡迎你們加羣討論RxHttp&RxLife 交流羣: 378530627