1. 程式人生 > >RxAndroid和Retrofit結合使用-網路呼叫和生命週期分析

RxAndroid和Retrofit結合使用-網路呼叫和生命週期分析

說明

這是我在專案使用RxAndroid、RxJava和Retrofit時的一些記錄和分析。

記錄1:

網路操作相關

在使用RxAndroid和Retrofit進行網路操作時,有如下這些程式碼

程式碼:

 getMyFollowingBoard(mTokenType, mTokenAccess, mIndex, mLimit)
                .doOnUnsubscribe(new Action0() {
                    @Override
                    public void call() {
                        Logger.d("Unsubscribe"
); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(只有響應操作的列印...

列印結果:

(正確聯網返回)

[ModuleFragment:onStart:230]:
[ModuleFragment:onNext:251]:
[ModuleFragment:onCompleted:236]:
[ModuleFragment:call:221]: Unsubscribe

分析:

  • 網路的呼叫在Fragment中,沒有做相關Fragment生命週期繫結處理。
  • 列印結果的前四行,沒有問題,是正確的呼叫。

疑問:

但是我沒有在任何地方呼叫取消訂閱的操作,為什麼會能夠響應doOnUnsubscribe方法?

有必要貼一下doOnUnsubscribe的說明圖

doOnUnsubscribe的說明圖

方法說明:Observables被取消訂閱時候呼叫。

總結:

在RxJavaCallAdapterFactory類裡面層層呼叫最後能看到這樣的程式碼

// Attempt to cancel the call if it is still in
-flight on unsubscription. subscriber.add(Subscriptions.create(new Action0() { @Override public void call() { call.cancel();
//這是Retrofit網路取消方法 } }));

這段程式碼是給 subscribe 增加一個 unsubscribe 的事件。 也就是請求完成的時候,會自動對 call 進行一個終止,也就是 http 的 close 行為。

記錄2:

有關網路呼叫和生命週期

程式碼:

修改上面的程式碼,新增執行緒休眠2秒,網路操作在onActivityCreated生命週期中呼叫

getMyFollowingBoard(mTokenType, mTokenAccess, mIndex, mLimit)
 .filter(new Func1<FollowingBoardListBean, Boolean>() {
                    @Override
                    public Boolean call(FollowingBoardListBean followingBoardListBean) {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        return true;
                    }
                })
                .doOnUnsubscribe(new Action0() {
                    @Override
                    public void call() {
                        Logger.d("Unsubscribe");
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(只有響應操作的列印...)

列印結果:

(進入Fragment,再馬上退出)

[BaseFragment:onCreateView:95]: MyAttentionBoardFragment
[BaseFragment:onActivityCreated:106]: MyAttentionBoardFragment
—>按下返回鍵,退出這個Fragment
[BaseFragment:onPause:124]: MyAttentionBoardFragment
[BaseFragment:onDestroyView:136]: MyAttentionBoardFragment
[BaseFragment:onDestroy:143]: MyAttentionBoardFragment
[MyAttentionBoardFragment:onNext:90]:
[MyAttentionBoardFragment:onCompleted:79]:
[MyAttentionBoardFragment:call:70]: Unsubscribe

分析:

  • 我在聯網的方法中添加了讓執行緒休眠2秒的方法,模擬網路狀態不好的情況下,聯網結果返回很慢。
  • 可以看到,我在onActivityCreated中開始聯網,由於延遲在馬上退出後,在我整個Fragment都已經銷燬,聯網結果才返回。

問題:

在負責顯示網路內容的Fragment都銷燬的情況還接收聯網結果呼叫是沒有意義的。沒有用的物件佔用記憶體。

解決辦法:

類說明:Subscription that represents a group of Subscriptions that are unsubscribed together.
All methods of this class are thread-safe.
翻譯:有關於Subscriptions的集合類,用於取消訂閱操作。

所以我們可以使用它儲存我們的Subscription聯網操作,在合適的地方取消
整合在BaseFragment用於統一管理。

程式碼:

public abstract class BaseFragment extends Fragment {
         private CompositeSubscription mCompositeSubscription;

    public CompositeSubscription getCompositeSubscription() {
        if (this.mCompositeSubscription == null) {
            this.mCompositeSubscription = new CompositeSubscription();
        }

        return this.mCompositeSubscription;
    }

    public void addSubscription(Subscription s) {
        if (this.mCompositeSubscription == null) {
            this.mCompositeSubscription = new CompositeSubscription();
        }

        this.mCompositeSubscription.add(s);
    }

     @Override
    public void onDestroy() {
        super.onDestroy();
          //在銷燬時統一取消
        if (this.mCompositeSubscription != null) {
            this.mCompositeSubscription.unsubscribe();
        }

    }

}

新增上面程式碼到BaseFragment後,再次實驗

列印結果:

[BaseFragment:onCreateView:95]: MyAttentionBoardFragment
[BaseFragment:onActivityCreated:106]: MyAttentionBoardFragment
[BaseFragment:onResume:118]: MyAttentionBoardFragment
[BaseFragment:onPause:124]: MyAttentionBoardFragment
[BaseFragment:onDestroy:143]: MyAttentionBoardFragment
[MyAttentionBoardFragment:call:72]: Unsubscribe //看到取消了
[BaseFragment:onDetach:155]: MyAttentionBoardFragment

總結:

這樣就實現了RxAndroid和Retrofit在Fragment的優化(Activity同理)

這在Goog官方MVP架構中,同樣採用這樣的方式處理生命週期同步。

記錄3

AndroidObservable

AndroidObservable.bindActivity(this, retrofitService.getImage(url))
    .subscribeOn(Schedulers.io())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap);

實現:當你的Activity或Fragment完成,同樣也會發出停止網路操作訊號

但是相信大家在compile 'io.reactivex:rxandroid:1.1.0'

  • 編譯之後是找不到AndroidObservable這個類。 http://stackoverflow.com/ 上也有很多問的人,為什麼找不到? 同學仔細看原文的釋出時間 2014年10月,2年前的博文啊!

原因

AppObservable and its bind methods have been completely eradicated. There were a number of problems with it:
說明:AppObservable(也就是進化版的AndroidObservable),存在很多問題,已經從RxAndroid中移除

問題如下:
1. It tried to auto-unsubscribe, but it would only do so if the sequence emitted an item after the Activity or Fragment paused. As a consequence, sequences that never end might never unsubscribe.
2. It was designed to defend against notifications after pause, but it appears that bug only occurred due to a subtle logic issue in the HandlerScheduler.
3. It automatically called observeOn(AndroidSchedulers.mainThread()), whether you wanted it or not.

最後:

  1. 官方推薦的RxLifecycle來管理生命週期,個人覺得RxLifecycle太死板,它需我們繼承extends RxAppCompatActivity,會影響我們的程式碼層級。
  2. Check whether you need to add observeOn(AndroidSchedulers.mainThread()) to the sequence.,好好寫程式碼吧。
  3. 目前使用CompositeSubscription來管理訂閱者的訂閱是比較常見的做法。當然是在有需要的情況下使用。