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 行爲。

資料來自於:
https://segmentfault.com/a/1190000004077117?utm_source=tuicool&utm_medium=referral

記錄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都銷燬的情況還接收聯網結果調用是沒有意義的。沒有用的對象佔用內存。

解決辦法:

在RxJava中存在這個類
rx.subscriptions.CompositeSubscription

類說明: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

在這篇博客http://blog.danlew.net/2014/10/08/grokking-rxjava-part-4/
也就是深入淺出RxJava(四:響應式安卓開發)的原文中又提到
這樣的代碼實現

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

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

這裏有AndroidObservable源碼

但是相信大家在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來管理訂閱者的訂閱是比較常見的做法。當然是在有需要的情況下使用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章