帶你學開源項目:RxLifecycle-當Activity被destory時自動暫停網絡請求

版權聲明:本文原創發佈於公衆號 wingjay,轉載請務必註明出處! https://blog.csdn.net/lanxian837820149/article/details/88628985

大幅提高自身技術實力最有效的途徑之一就是學習世界級優秀開源項目的精髓,而本人的《帶你學開源項目》系列文章將持續更新,對當前Android開發界最優秀的開源項目進行深入分析。

一、 介紹

本人的《帶你學開源項目系列文章》採取的分析思路不是從源碼裏抽代碼出來一步步跟蹤,瞭解其具體實現,而是通過提出問題,一步步思考解決方法,從而學習到開源項目的思維精華。筆者認爲這種方式更有利於讀者提高自身思維方式和技術能力

二、 開源項目

RxLifecycle 地址:https://github.com/trello/RxLifecycle 。該項目是爲了防止RxJavasubscription導致內存泄漏而誕生的,核心思想是通過監聽ActivityFragment的生命週期,來自動斷開subscription以防止內存泄漏。

基本用法如下:

myObservable
    .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
    .subscribe();

此處myObservable可以看成一個耗時的網絡請求,通過綁定到ActivityEvent.DESTROY,一旦Activity發生了DESTORY生命週期,數據就不會再流向subscriber,即不會對這些數據進行任何處理和UI繪製,從而提高安全性。

三、 問題

Android開發中常會有這樣一個場景:

  1. 發送網絡請求 -> 2. 服務器處理請求並返回數據 -> 3. client端接收數據,繪製UI。

在前兩步一般都是不會出現問題的,但是在第三步,當數據返回給client端時,如果頁面已經不在了,那麼就無法去繪製UI,很有可能會導致意向不到的問題。因此,爲了解決這個問題,一個好的思路就是當頁面離開時,自動斷開網絡請求數據的處理過程,即數據返回後不再進行任何處理

四、 思考

要達到上面這樣一個功能,我們可以思考,至少需要兩部分:

  1. 隨時監聽Activity(Fragment)的生命週期並對外發射出去;
  2. 在我們的網絡請求中,接收生命週期並進行判斷,如果該生命週期是自己綁定的,如Destory,那麼就斷開數據向下傳遞的過程。

五、 分析

可以看到,首先有一個核心功能要實現:就是既能夠監聽Activity生命週期事件並對外發射,又能夠接收每一個生命週期事件並作出判斷。爲了實現這個功能,可以聯想到RxJava中的Subject,既能夠發射數據,又能夠接收數據。

六、 Subject介紹

瞭解Subject的讀者可以跳過這部分。

如何理解Subject呢?

很容易,在RxJava裏面,Observable是數據的發射者,它會對外發射數據,然後經過mapflatmap等等數據處理後,最終傳遞給Observer,這個數據接收者。因此,拋開中間數據處理不管,可以看出,Observable對外發射數據,是數據流的開端;Observer接收數據,是數據流的末端。

那麼Subject呢?看一眼源碼:

/**
 * 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:

  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處理接收了兩個數據onetwo -> 最終這些數據都傳遞給了myObserver。所以,subject扮演的角色是:

數據onetwo => (Observer) subject (Observable) => myObserver

簡單來說,我們把數據onetwo塞給subject,然後subject又發射給了myObserver

七、 BaseActivity監聽生命週期

那麼我們先來實現生命週期監聽功能,基本思路是:在BaseActivity裏創建一個PublishSubject對象,在每個生命週期發生時,把該生命週期事件傳遞給PublishSubject。具體實現如下(只寫部分生命週期,其他類似):

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,接收生命週期並自動斷開自身

通常我們的一次網絡請求長這樣:

networkObservable
	.subscribe(new Observer(  handleUI()  ));

其中,networkObservable表示一個通用的網絡請求,會接收網絡數據並傳遞給Observer去繪製UI。

現在,我們希望這個networkObservable監聽ActivityDESTORY事件,一旦發生了DESTORY就自動斷開Observer,即使網絡數據回來了也不再傳遞給Observer去繪製UI。即:

networkObservable
	.compose(bindUntilEvent(ActivityLifeCycleEvent.DESTORY))
	.subscribe(new Observer(  handleUI()  ));

因此,我們需要實現

bindUntilEvent(ActivityLifeCycleEvent.DESTORY)

這個方法,那如何實現呢?

我們知道lifecycleSubject能夠發射生命週期事件了,那麼我們可以讓networkObservable去檢查lifecycleSubject發出的生命週期,如果和自己綁定的生命週期事件一樣,那就自動停掉即可。

九、 改裝networkObservable

對於networkObservable自動停掉,我們可以利用操作符

networkObservable.takeUntil(otherObservable)

它的作用是監聽otherObservable,一旦otherObservable對外發射了數據,就自動把networkObservable停掉;

otherObservable何時對外發射數據呢?當然是lifecycleSubject發射出的生命週期事件等於綁定的生命週期事件時,開始發射。

	otherObservable = lifecycleSubject.takeFirst(new Func1<ActivityLifeCycleEvent, Boolean>() {
              @Override
              public Boolean call(ActivityLifeCycleEvent activityLifeCycleEvent) {
                return activityLifeCycleEvent.equals(bindEvent);
              }
            });

其中的關鍵是判斷activityLifeCycleEvent.equals(bindEvent);,一旦條件滿足,otherObservable就對外發射數據,然後networkObservable就立即自動停掉。

十、 合併 生命週期監聽 與 networkObservable改良

  1. 在BaseActivity裏添加lifecycleSubject,並把每一個生命週期事件按時傳遞給lifecycleSubject
  2. 在BaseActivity裏添加一個bindUntilEvent方法:
  @NonNull
  @Override
  public <T> Observable.Transformer<T, T> bindUntilEvent(@NonNull final ActivityLifeCycleEvent event) {
    return new Observable.Transformer<T, T>() {
      @Override
      public Observable<T> call(Observable<T> sourceObservable) {
        Observable<ActivityLifeCycleEvent> compareLifecycleObservable =
            lifecycleSubject.takeFirst(new Func1<ActivityLifeCycleEvent, Boolean>() {
              @Override
              public Boolean call(ActivityLifeCycleEvent activityLifeCycleEvent) {
                return activityLifeCycleEvent.equals(event);
              }
            });
        return sourceObservable.takeUntil(compareLifecycleObservable);
      }
    };
  }
  1. 在任意一個網絡請求 networkObservable 處改良
networkObservable
	.compose(bindUntilEvent(ActivityLifeCycleEvent.DESTORY))
	.subscribe(new Observer(  handleUI()  ));

注意:

  1. 文中提到的networkObservable是網絡請求,但實際上這不限於網絡請求,任何耗時操作如文件io操作等都可以利用這個方法,來監聽生命週期並自動暫停。
  2. 對於Fragment中的處理方法也是類似。

謝謝。

wingjay

PS:本文原創發佈於微信公衆號「wingjay」,回覆關鍵字「程序員」獲取一份 15 本程序員經典電子書。
Android架構、技術感悟、個人成長

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章