Android RxLife 一款輕量級別的RxJava生命週期管理庫
RxLife是一款輕量級別的RxJava生命週期管理庫,程式碼侵入性極低,隨用隨取,不需要做任何準備工作,支援在Activity/Fragment 的任意生命週期方法斷開管道。
原理
RxLife通過Jetpack 下的 Lifecycle 獲取 Activity/Fragment 的生命週期變化,並通過 Observable.lift(ObservableOperator)
操作符,注入自己實現的Observer物件(該物件能感知 Activity/Fragment的生命週期變化),從而在 onSubscribe(Disposable d)
方法中拿到Disposable物件,隨後在相應的生命週期回撥裡執行 Disposable.dispose()
方法斷開管道,這樣就能將 lift
操作符上面的所有Disposable物件全部斷開。
為什麼要重複造輪子
熟悉RxJava的同學應該都知道 trello/RxLifecycle 專案,它在目前的3.0.0版本中通過 Lifecycle
感知Activity/Fragment 的生命週期變化,並通過 BehaviorSubject
類及 compose
、 takeUntil
操作符來實現管道的中斷,這種實現原理有一點不足的是,它在管道斷開後,始終會往下游傳送一個 onComplete
事件,這對於在 onComplete
事件中有業務邏輯的同學來說,無疑是致命的。那為什麼會這樣呢?因為 takeUntil
操作符內部實現機制就是這樣的,有興趣的同學可以去閱讀 takeUntil
操作符的原始碼,這裡不展開。而RxLife就不會有這樣問題,因為在原理上 RxLife
就與 trello/RxLifecycle
不同,並且RxLife還在 lift
操作都的基礎上提供了一些額外的api,能有效的避免因RxJava內部類持有Activity/Fragment的引用,而造成的記憶體洩漏問題,下面開始講解。
gradle依賴
implementation 'com.rxjava.rxlife:rxlife:1.0.3' 複製程式碼
用法
Observable.timer(10, TimeUnit.SECONDS) //預設在onDestroy時中斷管道 .lift(RxLife.lift(this)) .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); //或者 Observable.timer(10, TimeUnit.SECONDS) //指定在onStop時中斷管道 .lift(RxLife.lift(this,Event.ON_STOP)) .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); 複製程式碼
在Activity/Fragment 中,使用Observable的 lift()
操作符,方法中傳入 RxLife.lift(this)
,如果需要指定生命週期方法,額外再傳一個Event物件即可。怎麼樣??是不是極其簡單,根本不需要做任何準備工作,程式碼侵入性極低。
處理記憶體洩漏
我們來看一個案例
public void leakcanary(View view) { Observable.timer(100, TimeUnit.MILLISECONDS) .map(new MyFunction<>()) //阻塞操作 .lift(RxLife.lift(this)) .subscribe(new Consumer<Long>() { //這裡使用匿名內部類,持有Activity的引用 //注意這裡不能使用Lambda表示式,否則leakcanary檢測不到記憶體洩漏 @Override public void accept(Long aLong) throws Exception { Log.e("LJX", "accept =" + aLong); } }); } //這裡使用靜態內部類,不會持有外部類的引用 static class MyFunction<T> implements Function<T, T> { @Override public T apply(T t) throws Exception { //當dispose時,第一次睡眠會被吵醒,接著便會進入第二次睡眠 try { Thread.sleep(3000); } catch (Exception e) { } try { Thread.sleep(30000); } catch (Exception e) { } return t; } } 複製程式碼
上面的程式碼會造成Activity無法回收,導致記憶體洩漏,我們用Leakcannry工具來檢測一下,發現確實造成來記憶體洩漏,如下

RxLife
庫,會自動中斷管道,那為什麼還會造成記憶體洩漏呢?其實原因很簡單,我們只是中斷了管道,而沒有中斷上游對下游引用。看上面的截圖就能知道,上游始終持有下游的引用,而最下游的匿名內部類
Consumer
又持有了Activity的引用,所以就導致了Activity無法回收。
那為什麼中斷管道時,不會中斷上下游的引用呢?
首先有一點我們需要明確,呼叫 Disposable.dispose()
方法來斷開管道,並不是真正意義上的將上游與下游斷開,它只是改變了管道上各個Observer物件的一個標誌位的值,我們來看一下 LambdaObserver
類的原始碼就會知道
@Override public void dispose() { DisposableHelper.dispose(this); } 複製程式碼
呃呃,只有一行程式碼,我們繼續
public static boolean dispose(AtomicReference<Disposable> field) { Disposable current = field.get(); //此處得到上游的Disposable物件 Disposable d = DISPOSED; if (current != d) { current = field.getAndSet(d); //更改自己的標誌位為DISPOSED if (current != d) { if (current != null) { current.dispose();//關閉上游的Disposable物件 } return true; } } return false; } 複製程式碼
可以看到,這裡只做了兩件事,一是更改自己的標誌位,二是呼叫上游的 dispose()
方法,其實你只要多看看,你就發現,RxJava內部大多數Observer在 dispose()
方法都會幹這兩件事。
到這,我們該如何解決這個記憶體洩漏問題呢?其實,RxJava早就想到了這一點,它給我們提供了一個 onTerminateDetach()
操作符,這個操作符會在 onError(Throwable t)
、 onComplete()
、 dispose()
這個3個時刻,斷開上游對下游的引用,我們來看看原始碼,原始碼在 ObservableDetach
類中
@Override public void dispose() { Disposable d = this.upstream; this.upstream = EmptyComponent.INSTANCE;//上游重新賦值 this.downstream = EmptyComponent.asObserver();//下游重新賦值 d.dispose();//呼叫上游的dispose()方法 } @Override public void onError(Throwable t) { Observer<? super T> a = downstream; this.upstream = EmptyComponent.INSTANCE;//上游重新賦值 this.downstream = EmptyComponent.asObserver();//下游重新賦值 a.onError(t); //呼叫下游的onError方法 } @Override public void onComplete() { Observer<? super T> a = downstream; this.upstream = EmptyComponent.INSTANCE;//上游重新賦值 this.downstream = EmptyComponent.asObserver();//下游重新賦值 a.onComplete();//呼叫下游的onComplete方法 } 複製程式碼
到這,我們就知道該怎麼做了,下面這樣寫就安全了
Observable.timer(100, TimeUnit.MILLISECONDS) .map(new MyFunction<>())//阻塞操作 .onTerminateDetach() //管道斷開時,中斷上游對下游的引用 .lift(RxLife.lift(this)) //預設在onDestroy時斷開管道 .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); 複製程式碼
可是,每次都要這樣寫嗎?有沒有更簡單的,有,RxLife提供了 RxLife.compose(LifecycleOwner)
方法,內部就是將 onTerminateDetach
、 lift
這兩個操作符整合在了一起,接下來,看看如何使用
Observable.timer(100, TimeUnit.MILLISECONDS) .map(new MyFunction<>())//阻塞操作 //注意這裡使用compose操作符 .compose(RxLife.compose(this))//預設在onDestroy時中斷管道,並中斷下下游之間的引用 .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); 複製程式碼
如果需要指定生命週期的方法,也可以
Observable.timer(100, TimeUnit.MILLISECONDS) .map(new MyFunction<>())//阻塞操作 //注意這裡使用compose操作符 .compose(RxLife.compose(this, Event.ON_STOP))//指定在onStop時斷開管道 .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); } 複製程式碼
大多數情況下,我們希望觀察者能主執行緒進行回撥,也許你會這樣寫
Observable.timer(100, TimeUnit.MILLISECONDS) .map(new MyFunction<>())//阻塞操作 .observeOn(AndroidSchedulers.mainThread()) //在主執行緒回撥 .compose(RxLife.compose(this, Event.ON_STOP))//指定在onStop回撥時中斷管道,並中斷上下游引用 .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); 複製程式碼
如果你是用RxLife的話,就可以這樣寫,使用 RxLife.composeOnMain
方法
Observable.timer(100, TimeUnit.MILLISECONDS) .map(new MyFunction<>())//阻塞操作 //在主執行緒程序回撥,在onStop回撥時中斷管道,並中斷上下游引用 .compose(RxLife.composeOnMain(this, Event.ON_STOP)) .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); 複製程式碼
RxLife類就只有6個靜態方法,如下

注意,前方高能預警!!!!!!!
結合RxLife使用Observable的 lift
、 compose
操作符時,下游除了 subscribe
操作符外最好不要有其它的操作符,前面講過,當呼叫 Disposable.dispose()
時,它會往上一層一層的呼叫上游的 dispose()
方法,如果下游有 Disposable
物件,是呼叫不到的,如果此時下游有自己的事件需要傳送,那麼就無法攔截了。 如:
Observable.just(1) .compose(RxLife.compose(this)) .flatMap((Function<Integer, ObservableSource<Long>>) integer -> { //每隔一秒傳送一個數據,共10個 return Observable.intervalRange(0, 10, 0, 1, TimeUnit.SECONDS); }) .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); 複製程式碼
這樣,即使Activity關閉了,觀察者每隔一秒後,依然能收到來自上游的事件,因為 compose
無法切斷下游的管道,我們改一下上面的程式碼
Observable.just(1) .flatMap((Function<Integer, ObservableSource<Long>>) integer -> { //每隔一秒傳送一個數據,共10個 return Observable.intervalRange(0, 10, 0, 1, TimeUnit.SECONDS); }) .compose(RxLife.compose(this)) .subscribe(aLong -> { Log.e("LJX", "accept =" + aLong); }); 複製程式碼
這樣ok了,其實這不是 RxLife
的問題,使用鼎鼎大名的 trello/RxLifecycle
庫也是一樣的,因為RxJava的設計就是如此,上游拿不到下游的 Disposable
物件,所以,我們在使用 RxLife
時,一定要注意在 lift
或者 compose
操作符的下游,除了 subscribe
操作符外最好不要有其它的操作符,這一點一定需要注意。
小彩蛋
RxLife類裡面的6個靜態方法,皆適用於Flowable、Observable、Single、Maybe、Completable這5個被觀察者物件,道理都一樣,這裡不在一一講解。
結尾
Ok,RxLife的使用基本就介紹完了,到這我們會發現,使用RxLife庫,我們只需要關注一個類即可,那即是RxLife類,api簡單功能卻強大。敢興趣的同學,可以去閱讀 RxLife
的 原始碼 ,有疑問,請留言,我會在第一時間作答。
RxLife 結合 HttpSender 傳送請求,簡直不要太爽。
HttpSender詳情請點選 HttpSender OkHttp+RxJava超好用、功能超級強大的Http請求框架