Android 使用RxLifecycle解决RxJava内存泄漏

RxLifecycle GitHub地址

RxJava GitHub地址

1.为什么会发生内存泄漏

使用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之后的内存占用情况:

打开Activity之后的内存占用情况

关闭Activity,手动执行GC(点击小车图标),发现内存占用并没有减少:

手动执行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.实践

看下使用RxLifecycle需要依赖的库:

// RxLifecycle基础库
compile 'com.trello.rxlifecycle2:rxlifecycle:2.1.0'

// Android使用的库,里面使用了Android的生命周期方法
// 内部引用了基础库,如果使用此库则无需再引用基础库
compile 'com.trello.rxlifecycle2:rxlifecycle-android:2.1.0'

// Android组件库,里面定义了例如RxAppCompatActivity、RxFragment之类的Android组件
// 内部引用了基础库和Android库,如果使用此库则无需再重复引用
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基础库
compile 'com.trello.rxlifecycle2:rxlifecycle-kotlin:2.1.0'

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

本文需要依赖其中两个库:

// 依赖以下两个库,会自动引用基础库与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方法被调用后就会解除绑定,以此类推。

有一种特殊情况,如果在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.<Long>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();

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> {
}

4.写在最后

源码已托管到GitHub上,欢迎Fork,觉得还不错就Start一下吧!

GitHub传送门

欢迎同学们吐槽评论,如果你觉得本篇博客对你有用,那么就留个言或者点下喜欢吧(^-^)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容