打造RxJava生命週期管理框架RxLife

在前邊RxJava實戰技巧大全一文中,我們介紹了RxJava使用過程中常見的應用場景和可能遇到的問題,其中我們談到利用RxLifeCycle來管理RxJava的生命週期,避免內存泄漏問題,今天自己動手打生命週期管理框RxLife來加深對RxJava的認識。


詳解Subject

什麼是Subject

在RxJava當中,有四個對象值得我們關注:Observable,Subject,Observer,Subscriber,它們之間的關係如下:
這裏寫圖片描述

對於Observable,Observer,Subscriber我們比較熟悉,故不做說明,重點來看Subject。

通過上面的圖我們可以看出Subject繼承自Observable,也就意味着Subject可以作爲被觀察者,另外,它又實現了Observer接口,這意味着它也可以作爲觀察者。不難看出,Subject既能作爲Observer訂閱Observable,又能作爲Observable被其他Observer訂閱。總之,Subject承擔了這麼一種角色:對上作爲觀察者,對下作爲被觀察者。

和Observable必須有訂閱者才能發射數據不一樣,無論Subject是否有訂閱者,它都可以發射數據。這有點類似廣播電臺,不會因爲我們關閉收音機就停止廣播,在收聽的人自然收聽的到,沒收聽的人也無關緊要。

常見的Subject

從上面的uml中我們看出,RxJava爲我們提供了四種常用的Subject,即
AsyncSubject,BehabviorSubject,PublishSubject,ReplaySubject,下面我們對這四者進行說明:

AsyncSubject

AsyncSubject會緩存最後一個數據並在調用onCompleted()時將該數據發送給訂閱者,原理如下:
這裏寫圖片描述

在該過程中,一旦發生任何異常都不會發送數據到訂閱者,而是發送給訂閱者一個異常通知,即訂閱者只能接受到一個異常的通知,如下:
這裏寫圖片描述

舉例來說明AsyncSubject的用法:

asyncSubject.onNext("1");
asyncSubject.onNect("2");
asyncSubject.onCompleted();//必須調用纔會開始發送數據

以上代碼執行後,訂閱者接受到的數據是2.

BehaviorSubject

當BehaviorSubject被訂閱後,它首先會發送原始Observable最近發射的數據,如果最近沒有,會發射一個默認值,接下繼續發射原始Observable的數據,如下圖:
這裏寫圖片描述

如果原始的Observable因爲發生了錯誤而終止,那麼BehaviorSubject在發送一個錯誤通知後不再發射數據,如下:
這裏寫圖片描述

我們舉例來說明BehabviorSubject的用法:

behaviorSubject.onNext("1");
behaviorSubject.onNect("2");
behaviorSubject.onNext("3");
behaviorSubject.subscribe(new Action<String>(){
    @Override
    public void call(String s){
        System.out.println(“result:”+s);
    }
});
behaviorSubject.onNext("4");

輸出結果是3,4.

PublishSubject

默認情況下,RxJava中的Observable一旦被訂閱就開始發送事件,這和我們傳統的觀察者模式有所區別。而PublishSuject的行爲則類似傳統的觀察這模式,觀察者可以先訂閱被觀察者,然後在某個時刻手動調用方法來發射數據(訂閱之後的數據)到所有的觀察者。如下圖:
這裏寫圖片描述

如果原始的Observable因爲發生了錯誤而終止,那麼PublishSubject在發送一個錯誤通知後不再發射數據,如下:
這裏寫圖片描述

舉例來說明PublishSubject的用法:

publishSubject.onNext("1");
publishSubject.onNect("2");
publishSubject.onNext("3");//訂閱之前不會被髮送

publishSubject.subscribe(new Action<String>(){
    @Override
    public void call(String s){
        System.out.println(“result:”+s);
    }
});
publishSubject.onNect("4");
publishSubject.onNect("5");

1,2,3是在訂閱之前的數據,不會被髮射,最終輸出結果是4,5。

ReplaySubject

ReplaySubject會緩存所有已經發射的數據,當一個新的訂閱關係產生時,ReplaySuject會將所有數據都發送給他。另外,ReplaySubject支持設置緩存數據和緩存時間。如下圖:
這裏寫圖片描述

舉例來說明ReplaySubject的用法:

replaySubject.onNext("1");
replaySubject.onNect("2");
replaySubject.onNext("3");

replaySubject.subscribe(new Action<String>(){
    @Override
    public void call(String s){
        System.out.println(“result:”+s);
    }
});
replaySubject.onNect("4");

默認情況下ReplaySubject會緩存所有的數據,因此最終數據的結果如下:

result:1
result:2
result:3
result:4

小結

回顧上面所談的,不難看出不同的Subject最大的區別在於發送數據的行爲不同,簡單概括如下:

Subject 發射行爲
AsyncSubject 不論訂閱發生在什麼時候,只會發射最後一個數據
BehaviorSubject 發送訂閱之前一個數據和訂閱之後的全部數據
ReplaySubject 不論訂閱發生在什麼時候,都發射全部數據
PublishSubject 發送訂閱之後全部數據

關於Subject更詳細的使用方法請直接查閱api doc.


實現生命週期管理框架(RxLife)

在瞭解Subject之後就可以開始考慮如何實現一個生命週期管理框架。每當Activity或者Fragment的生命週期發生變化時我們都希望產生一個對應的事件來通知當前所有的訂閱者,這樣我們就可以根據對應的事件去確定是否取消訂閱關係了。

從上面的描述中,我們有兩個問題要解決:

  1. 如何監聽Activity或Fragmeng生命週期變化並將其發送出去。
  2. 原有的觀察者如何接受生命週期,並在某生命週期下中斷原有的Observable。

通過以上兩個問題,我們知道我們需要一個既能夠發射生命週期,又能接受生命週期的觀察者,因此不難想到這裏需要Subject。生命週期是連續產生的,無論是否有訂閱者,我們只關注最最近的生命週期,因此我們選擇使用BehaviorSubject。

現在我們來考慮如何監聽Activity或Fragment的生命週期,並利用BehaviorSubject發射生命週期。這裏我們以Activity爲例進行說明。

生命週期事件監聽

定義生命週期事件

我們根據Activity的生命週期,定義相應的事件。

public enum  ActivityEvent {
    CREATE,
    RESUME,
    START,
    PAUSE,
    STOP,
    DESTORY
}

監聽生命週期

爲了能在Activitiy生命週期變化時發送相應的事件,我們定義了RxAppcompatActivity,該類繼承了AppCompatActivity並重寫器生命週期方法:在不同方法中發射事件到BehaviorSubject中。這就好像我們的BehaviorSubject對象在不斷的觀察Activity生命週期的變化。當然,由於Subject的特性,BehaviorSubject也具備了將這些事件發射出去的能力。

public class RxAppCompatActivity extends AppCompatActivity {
    protected final BehaviorSubject<ActivityEvent> lifeSubject = BehaviorSubject.create();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        lifeSubject.onNext(ActivityEvent.CREATE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        lifeSubject.onNext(ActivityEvent.RESUME);
    }

    @Override
    protected void onStart() {
        super.onStart();
        lifeSubject.onNext(ActivityEvent.START);
    }

    @Override
    protected void onPause() {
        super.onPause();
        lifeSubject.onNext(ActivityEvent.PAUSE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        lifeSubject.onNext(ActivityEvent.STOP);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        lifeSubject.onNext(ActivityEvent.DESTORY);
    }
}

Observable自動停止發射數據

到現在我們已經利用Subject來監視生命週期的變化,那又如何讓原有的Observable(比如網絡請求的Observable)來監視Subject發射的數據呢,並根據Subject的狀態自動停止原始數據的發射?換言之就是一個Observable如何在發射數據的同時監視另一個Observable?

TakeUtil操作符

令人高興的是,RxJava中提供的TakeUntil操作符來實現上述需求。TakeUntil訂閱原始的Observable併發射數據,此外它還監視你提供的第二個Observable。當第二個Observable發射了一項數據或者發射一項終止的通知時(onError通知或者onCompleted通知),TakeUntil返回的Observable會停止發射原始的Observable,如下圖所示:
這裏寫圖片描述

我們用一個簡單的例子來展示TakeUntil操作符的使用:

 Observable.interval(2, TimeUnit.SECONDS).subscribe(new Action1<Long>() {
            @Override
            public void call(Long num) {
                Log.d("MainActivity", "num:" + num);
            }
        });

上面的代碼每隔2s進行輸出,現在我們希望5s後自動停止輸出,就可以這樣做:

   Observable.interval(2, TimeUnit.SECONDS).takeUntil(Observable.timer(5,TimeUnit.SECONDS)).subscribe(new Action1<Long>() {
            @Override
            public void call(Long num) {
                Log.d("MainActivity", "num:" + num);
            }
        });

爲了讓以上代碼更通用,我們利用compose操作符進行改寫(對compose不熟悉的童鞋自行查閱資料):

private void startIntervalTask1() {
        Observable.interval(2, TimeUnit.SECONDS).compose(bindUntilDelay(5)).subscribe(new Action1<Long>() {
            @Override
            public void call(Long num) {
                Log.d("MainActivity", "num:" + num);
            }
        });

    }

    @NonNull
    private Observable.Transformer<Long, Long> bindUntilDelay(final int delaySecond) {
        return new Observable.Transformer<Long, Long>() {
            @Override
            public Observable<Long> call(Observable<Long> longObservable) {
                return longObservable.takeUntil(timer(delaySecond,TimeUnit.SECONDS));
            }
        };
    }

回到正題,現在我們已經有了可以發射生命週期事件的BehaviorSubject,再結合TakeUntil不就可以實現在指定生命週期發生時自動停止原有的Observable了嗎?

結合BehaviorSubject與TakeUntil

有了上面的知識做鋪墊,實現生命週期管理框架也就顯得輕而易舉了。
爲了方便使用,我們在RxAppcompatActivity中提供了bindUntilEvent(ActivityEvent nindEvent)方法:

public class RxAppCompatActivity extends AppCompatActivity {
    protected final BehaviorSubject<ActivityEvent> lifeSubject = BehaviorSubject.create();

    public <T> Observable.Transformer<T, T> bindUntilEvent(final ActivityEvent bindEvent) {
        //被監視的Observable
        final Observable<ActivityEvent> observable = lifeSubject.takeFirst(new Func1<ActivityEvent, Boolean>() {
            @Override
            public Boolean call(ActivityEvent event) {
                return event.equals(bindEvent);
            }
        });

        return new Observable.Transformer<T, T>() {
            @Override
            public Observable<T> call(Observable<T> sourceOb) {
                return sourceOb.takeUntil(observable);
            }
        };
    }


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        lifeSubject.onNext(ActivityEvent.CREATE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        lifeSubject.onNext(ActivityEvent.RESUME);
    }

    @Override
    protected void onStart() {
        super.onStart();
        lifeSubject.onNext(ActivityEvent.START);
    }

    @Override
    protected void onPause() {
        super.onPause();
        lifeSubject.onNext(ActivityEvent.PAUSE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        lifeSubject.onNext(ActivityEvent.STOP);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        lifeSubject.onNext(ActivityEvent.DESTORY);
    }
}

接下來,我們用同樣的方式來處理Fragment或者其他組件即可。

具體使用

新建的Activity需要繼承我們的RxAppcompatActivity,新建的Fragment則繼承我們的RxFragment,就是這麼簡單。

我們同樣還是以師父說爲例,由於我們的方法基本和RxLifeCycle保持一致,因此只要簡單的改動就可以讓RxLife工作起來,現在就可以用RxLife來代替RxLifeCycle。

仍然做個簡單的示例:

ApiFactory.getWXApi().getWXHot(AppConstant.KEY_WX, getPageSize(), mCurrentPage + 1).compose(this.bindUntilEvent(FragmentEvent.STOP))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(mSubscriber);

總結

通過自行實現一個RxJava生命週期管理框架(RxLife)加深le我們對RxJava中Subject的理解。另外,Subject的應用非常廣泛,在下篇文章中,我們將會進一步深入,利用Subject來打造自己的事件通信總線RxBus。

githubhttps://github.com/closedevice/FastApp

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