1. 程式人生 > >RxLifecycle解決RxJava記憶體洩漏

RxLifecycle解決RxJava記憶體洩漏

1.RxJava導致記憶體洩漏

使用RxJava釋出一個訂閱後,當頁面被finish,此時訂閱邏輯還未完成,如果沒有及時取消訂閱,就會導致Activity/Fragment無法被回收,從而引發記憶體洩漏。
寫段程式碼測試一下,定義一個Activity,佈局中顯示一張圖片,這樣可以直觀的看到此Activity的記憶體佔用情況,然後在Activity中釋出一個訂閱後,關閉Activity,訂閱邏輯如下:

// 每隔1s執行一次事件
Observable.interval(1, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Observer<Long>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {

            }

            @Override
            public void onNext(@NonNull Long aLong) {
                Log.i("接收資料", String.valueOf(aLong));
            }

            @Override
            public void onError(@NonNull Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
看下開啟Activity之前的記憶體佔用情況:


開啟Activity之後的記憶體佔用情況:


關閉Activity,手動執行GC(點選小車圖示),發現記憶體佔用並沒有減少:


匯出hprof檔案進行分析(點選小車圖示右邊的圖示),發現已經發生了記憶體洩漏:


那麼除了在onDestory方法中手動取消訂閱之外,還有什麼方法可以避免上述的洩漏問題呢,這時RxLifecycle就派上用場了。

2.RxLifeCycle的概念

官方介紹:

This library allows one to automatically complete sequences based on a second lifecycle stream.

This capability is useful in Android, where incomplete subscriptions can cause memory leaks.

3.實踐

需要依賴的庫:

// 依賴以下兩個庫,會自動引用基礎庫與Android庫
compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'
compile 'com.trello.rxlifecycle2:rxlifecycle-navi:2.1.0'

3.1 rxlifecycle-components
public class RxLifecycleComponentsActivity extends RxAppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rxlifecycle_components);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Observable.interval(1, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(this.<Long>bindToLifecycle())
                .subscribe();
    }
}

使用compose(this.bindToLifecycle())方法繫結Activity的生命週期,在onStart方法中繫結,在onStop方法被呼叫後就會解除繫結,以此類推。

有一種特殊情況,如果在onPause/onStop方法中繫結,那麼就會在它的下一個生命週期方法(onStop/onDestory)被呼叫後解除繫結。

再次執行程式,開啟關閉Acitivity,手動進行GC,發現佔用記憶體減少了,匯出hprof檔案進行分析,沒有發生記憶體洩漏。
除了使用bindToLifecycle的方式之外,還可以指定取消訂閱的時機:

public class RxLifecycleComponentsActivity extends RxAppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rxlifecycle_components);
        ButterKnife.bind(this);

        initData();
    }

    private void initData() {
        Observable.interval(1, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(this.<Long>bindUntilEvent(ActivityEvent.DESTROY))
                .subscribe();
    }
}
使用compose(this.bindUntilEvent(ActivityEvent.DESTROY))方法指定在onDestroy方法被呼叫時取消訂閱。
注意compose方法需要在subscribeOn方法之後使用,因為在測試的過程中發現,將compose方法放在subscribeOn方法之前,如果在被觀察者中執行了阻塞方法,比如Thread.sleep(),取消訂閱後該阻塞方法不會被中斷。
Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<String> e) throws Exception {
                try {
                    Thread.sleep(60 * 1000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        });
        observable
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(this.<String>bindUntilEvent(ActivityEvent.DESTROY))
                .subscribe();
3.2 rxlifecycle-navi
public class RxLifecycleNaviActivity extends NaviActivity {

    private final LifecycleProvider<ActivityEvent> provider
            = NaviLifecycle.createActivityLifecycleProvider(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rxlifecycle_navi);
        ButterKnife.bind(this);

        initData();
    }

    private void initData() {
        Observable.interval(1, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(provider.<Long>bindUntilEvent(ActivityEvent.DESTROY))
                .subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {

                    }

                    @Override
                    public void onNext(@NonNull Long aLong) {
                        Log.i("接收資料", String.valueOf(aLong));
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Observable.interval(1, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(provider.<Long>bindToLifecycle())
                .subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {

                    }

                    @Override
                    public void onNext(@NonNull Long aLong) {
                        Log.i("接收資料", String.valueOf(aLong));
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }
}

繼承了NaviActivity,通過NaviLifecycle.createActivityLifecycleProvider(this)方法獲取LifecycleProvider物件,有了LifecycleProvider物件之後就可以呼叫bindToLifecycle或者bindUntilEvent方法了。

如果你使用的是MVP結構,這個LifecycleProvider物件可以直接傳給Presenter層使用。

繼承RxAppCompatActivity為什麼就能直接用this的方式呼叫bindToLifecycle或bindUntilEvent方法呢?看下原始碼,原來RxAppCompatActivity實現了LifecycleProvider介面。

public abstract class RxAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
}