Android 使用RxLifecycle解決RxJava內(nèi)存泄漏

RxLifecycle GitHub地址

RxJava GitHub地址

1.為什么會發(fā)生內(nèi)存泄漏

使用RxJava發(fā)布一個訂閱后,當(dāng)頁面被finish,此時訂閱邏輯還未完成,如果沒有及時取消訂閱,就會導(dǎo)致Activity/Fragment無法被回收,從而引發(fā)內(nèi)存泄漏。

寫段代碼測試一下,定義一個Activity,布局中顯示一張圖片,這樣可以直觀的看到此Activity的內(nèi)存占用情況,然后在Activity中發(fā)布一個訂閱后,關(guān)閉Activity,訂閱邏輯如下:

// 每隔1s執(zhí)行一次事件
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("接收數(shù)據(jù)", String.valueOf(aLong));
            }

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

            }

            @Override
            public void onComplete() {

            }
        });

看下打開Activity之前的內(nèi)存占用情況:

打開Activity之前的內(nèi)存占用情況

打開Activity之后的內(nèi)存占用情況:

打開Activity之后的內(nèi)存占用情況

關(guān)閉Activity,手動執(zhí)行GC(點擊小車圖標(biāo)),發(fā)現(xiàn)內(nèi)存占用并沒有減少:

手動執(zhí)行GC

導(dǎo)出hprof文件進行分析(點擊小車圖標(biāo)右邊的圖標(biāo)),發(fā)現(xiàn)已經(jīng)發(fā)生了內(nèi)存泄漏:

內(nèi)存泄漏分析

那么除了在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.

大概意思就是:可以通過綁定生命周期的方式,來解決內(nèi)存泄漏的問題。

3.實踐

看下使用RxLifecycle需要依賴的庫:

// RxLifecycle基礎(chǔ)庫
compile 'com.trello.rxlifecycle2:rxlifecycle:2.1.0'

// Android使用的庫,里面使用了Android的生命周期方法
// 內(nèi)部引用了基礎(chǔ)庫,如果使用此庫則無需再引用基礎(chǔ)庫
compile 'com.trello.rxlifecycle2:rxlifecycle-android:2.1.0'

// Android組件庫,里面定義了例如RxAppCompatActivity、RxFragment之類的Android組件
// 內(nèi)部引用了基礎(chǔ)庫和Android庫,如果使用此庫則無需再重復(fù)引用
compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'

// Android使用的庫,繼承NaviActivity使用
compile 'com.trello.rxlifecycle2:rxlifecycle-navi:2.1.0'

// Android使用的庫,繼承LifecycleActivity使用
// 需要引入Google的倉庫支持,用法和rxlifecycle-navi類似
compile 'com.trello.rxlifecycle2:rxlifecycle-android-lifecycle:2.1.0'

// Google的倉庫支持
allprojects {
    repositories {
        jcenter()
        maven { url 'https://dl.google.com/dl/android/maven2/' }
    }
}

// 支持Kotlin語法的RxLifecycle基礎(chǔ)庫
compile 'com.trello.rxlifecycle2:rxlifecycle-kotlin:2.1.0'

// 支持Kotlin語法的Android庫
compile 'com.trello.rxlifecycle2:rxlifecycle-android-lifecycle-kotlin:2.1.0'

本文需要依賴其中兩個庫:

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

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.<Long>bindToLifecycle())方法綁定Activity的生命周期,在onStart方法中綁定,在onStop方法被調(diào)用后就會解除綁定,以此類推。

有一種特殊情況,如果在onPause/onStop方法中綁定,那么就會在它的下一個生命周期方法(onStop/onDestory)被調(diào)用后解除綁定。

再次運行程序,打開關(guān)閉Acitivity,手動進行GC,發(fā)現(xiàn)占用內(nèi)存減少了,導(dǎo)出hprof文件進行分析,沒有發(fā)生內(nèi)存泄漏。

除了使用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.<Long>bindUntilEvent(ActivityEvent.DESTROY))方法指定在onDestroy方法被調(diào)用時取消訂閱。

注意compose方法需要在subscribeOn方法之后使用,因為在測試的過程中發(fā)現(xiàn),將compose方法放在subscribeOn方法之前,如果在被觀察者中執(zhí)行了阻塞方法,比如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();

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("接收數(shù)據(jù)", 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("接收數(shù)據(jù)", String.valueOf(aLong));
                    }

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

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }
}

繼承了NaviActivity,通過NaviLifecycle.createActivityLifecycleProvider(this)方法獲取LifecycleProvider對象,有了LifecycleProvider對象之后就可以調(diào)用bindToLifecycle或者bindUntilEvent方法了。

如果你使用的是MVP結(jié)構(gòu),這個LifecycleProvider對象可以直接傳給Presenter層使用。

繼承RxAppCompatActivity為什么就能直接用this的方式調(diào)用bindToLifecycle或bindUntilEvent方法呢?看下源碼,原來RxAppCompatActivity實現(xiàn)了LifecycleProvider接口。

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

4.寫在最后

源碼已托管到GitHub上,歡迎Fork,覺得還不錯就Start一下吧!

GitHub傳送門

歡迎同學(xué)們吐槽評論,如果你覺得本篇博客對你有用,那么就留個言或者點下喜歡吧(^-^)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容