此文需要讀者對RxJava有一定了解
一、 介紹
本文分析思路不是從原始碼裡抽程式碼出來一步步跟蹤,而是提出問題,一步步思考解決方法,從而學習到開源專案的思維精華,而不僅僅是瞭解該專案的具體實現。筆者認為這種方式更有利於讀者提高自身思維方式和技術能力。
二、 開源專案
RxLifecycle 地址:https://github.com/trello/RxLifecycle 。該專案是為了防止RxJava
中subscription
導致記憶體洩漏而誕生的,核心思想是通過監聽Activity
、Fragment
的生命週期,來自動斷開subscription
以防止記憶體洩漏。
基本用法如下:
1 2 3 |
myObservable .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) .subscribe(); |
此處myObservable
可以看成一個耗時的網路請求,通過繫結到ActivityEvent.DESTROY
,一旦Activity發生了DESTORY
生命週期,資料就不會再流向subscriber
,即不會對這些資料進行任何處理和UI繪製,從而提高安全性。
三、 問題
Android開發中常會有這樣一個場景:
- 傳送網路請求 -> 2. 伺服器處理請求並返回資料 -> 3. client端接收資料,繪製UI。
在前兩步一般都是不會出現問題的,但是在第三步,當資料返回給client端時,如果頁面已經不在了,那麼就無法去繪製UI,很有可能會導致意向不到的問題。因此,為了解決這個問題,一個好的思路就是當頁面離開時,自動斷開網路請求資料的處理過程,即資料返回後不再進行任何處理
。
四、 思考
要達到上面這樣一個功能,我們可以思考,至少需要兩部分:
- 隨時監聽
Activity
(Fragment
)的生命週期並對外發射出去; - 在我們的網路請求中,接收生命週期並進行判斷,如果該生命週期是自己繫結的,如
Destory
,那麼就斷開資料向下傳遞的過程。
五、 分析
可以看到,首先有一個核心功能要實現:就是既能夠監聽Activity
生命週期事件並對外發射,又能夠接收每一個生命週期事件並作出判斷。為了實現這個功能,可以聯想到RxJava
中的Subject
,既能夠發射資料,又能夠接收資料。
六、 Subject介紹
瞭解Subject
的讀者可以跳過這部分。
如何理解Subject
呢?
很容易,在RxJava裡面,Observable
是資料的發射者,它會對外發射資料,然後經過map
、flatmap
等等資料處理後,最終傳遞給Observer
,這個資料接收者。因此,拋開中間資料處理不管,可以看出,Observable
對外發射資料,是資料流的開端;Observer
接收資料,是資料流的末端。
那麼Subject
呢?看一眼原始碼:
1 2 3 4 |
/** * Represents an object that is both an Observable and an Observer. */ public abstract class Subject<T, R> extends Observable<R> implements Observer<T> {} |
首先,它extends Observable<R>
,說明Subject
具備了對外發射資料的能力,即擁有了from()
、just()
等等;另外,它又implements Observer<T>
,說明又能夠處理資料,具備onNext()
、onCompleted
等等。
然後,Subject
畢竟只是一個抽象類,那麼我們要如何使用它呢?
這裡介紹一種最簡單的:PublishSubject
:
1 2 3 4 5 6 |
PublishSubject<Object> subject = PublishSubject.create(); // myObserver will receive "one" & "two" and onCompleted events subject.subscribe(myObserver); subject.onNext("one"); subject.onNext("two"); subject.onCompleted(); |
這裡做的事情很簡單,先建立一個PublishSubject
-> 繫結一個myObserver
,此時subject
扮演了Observable
的角色,把資料發射給myObserver
-> 然後subject
處理接收了兩個資料one
、two
-> 最終這些資料都傳遞給了myObserver
。所以,subject
扮演的角色是:
資料one
、two
=> (Observer) subject
(Observable) => myObserver
簡單來說,我們把資料one
、two
塞給subject
,然後subject
又發射給了myObserver
。
七、 BaseActivity監聽生命週期
那麼我們先來實現生命週期監聽功能,基本思路是:在BaseActivity
裡建立一
個PublishSubject
物件,在每個生命週期發生時,把該生命週期事件傳遞給PublishSubject
。具體實現如下(只寫部分生命週期,其他類似):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class BaseActivity { protected final PublishSubject<ActivityLifeCycleEvent> lifecycleSubject = PublishSubject.create(); @Override protected void onCreate(Bundle savedInstanceState) { lifecycleSubject.onNext(ActivityLifeCycleEvent.CREATE); ... } @Override protected void onPause() { lifecycleSubject.onNext(ActivityLifeCycleEvent.PAUSE); ... } @Override protected void onStop() { lifecycleSubject.onNext(ActivityLifeCycleEvent.STOP); ... } ... } |
這樣的話,我們把所有生命週期事件都傳給了lifecycleSubject
了,或者說,lifecycleSubject
已經接收到了並能夠對外發射各種生命週期事件
的能力了。
八、 改良每一個Observable,接收生命週期並自動斷開自身
通常我們的一次網路請求長這樣:
1 2 |
networkObservable .subscribe(new Observer( handleUI() )); |
其中,networkObservable
表示一個通用的網路請求,會接收網路資料並傳遞給Observer
去繪製UI。
現在,我們希望這個networkObservable
監聽Activity
的DESTORY
事件,一旦發生了DESTORY
就自動斷開Observer
,即使網路資料回來了也不再傳遞給Observer
去繪製UI。即:
1 2 3 |
networkObservable .compose(bindUntilEvent(ActivityLifeCycleEvent.DESTORY)) .subscribe(new Observer( handleUI() )); |
因此,我們需要實現
1 |
bindUntilEvent(ActivityLifeCycleEvent.DESTORY) |
這個方法,那如何實現呢?
我們知道lifecycleSubject
能夠發射生命週期事件了,那麼我們可以讓networkObservable
去檢查lifecycleSubject
發出的生命週期,如果和自己繫結的生命週期事件一樣,那就自動停掉即可。
九、 改裝networkObservable
對於networkObservable自動停掉
,我們可以利用操作符
1 |
networkObservable.takeUntil(otherObservable) |
它的作用是監聽otherObservable
,一旦otherObservable
對外發射了資料,就自動把networkObservable
停掉;
那otherObservable
何時對外發射資料呢?當然是lifecycleSubject
發射出的生命週期事件等於
繫結的生命週期事件時,開始發射。
1 2 3 4 5 6 |
otherObservable = lifecycleSubject.takeFirst(new Func1<ActivityLifeCycleEvent, Boolean>() { @Override public Boolean call(ActivityLifeCycleEvent activityLifeCycleEvent) { return activityLifeCycleEvent.equals(bindEvent); } }); |
其中的關鍵是判斷activityLifeCycleEvent.equals(bindEvent);
,一旦條件滿足,otherObservable
就對外發射資料,然後networkObservable
就立即自動停掉。
十、 合併 生命週期監聽 與 networkObservable改良
- 在BaseActivity裡新增
lifecycleSubject
,並把每一個生命週期事件按時傳遞給lifecycleSubject
- 在BaseActivity裡新增一個
bindUntilEvent
方法:
1234567891011121314151617@NonNull@Overridepublic <T> Observable.Transformer<T, T> bindUntilEvent(@NonNull final ActivityLifeCycleEvent event) {return new Observable.Transformer<T, T>() {@Overridepublic Observable<T> call(Observable<T> sourceObservable) {Observable<ActivityLifeCycleEvent> compareLifecycleObservable =lifecycleSubject.takeFirst(new Func1<ActivityLifeCycleEvent, Boolean>() {@Overridepublic Boolean call(ActivityLifeCycleEvent activityLifeCycleEvent) {return activityLifeCycleEvent.equals(event);}});return sourceObservable.takeUntil(compareLifecycleObservable);}};} - 在任意一個網路請求 networkObservable 處改良
123networkObservable.compose(bindUntilEvent(ActivityLifeCycleEvent.DESTORY)).subscribe(new Observer( handleUI() ));
注意:
- 文中提到的
networkObservable
是網路請求,但實際上這不限於網- 絡請求,任何耗時操作如檔案io操作等都可以利用這個方法,來監聽生命週期並自動暫停。
- 對於
Fragment
中的處理方法也是類似。
謝謝!
如果你想要第一時間獲取我的文章,歡迎關注公眾號 CoolCoder,跟隨我一起學習技術、感悟藝術、體會生活吧。如果你對開源專案分析感興趣,歡迎加入我們的android-open-source-project-cracking
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式