使用过Rxjava的小伙伴都知道,在使用RxJava时如果处理不当,很可能会产生内存泄漏的问题。 
我们使用rxjava最大的原因是响应式编程使我们的异步操作代码变得很优雅,在Android中,也使线程切换变得很简单,而产生内存泄漏的大部分原因都是在异步执行耗时操作时,我们关闭了Activity,但是由于rxjava仍然持有Activity的引用,导致Activity无法被内存回收。这样就造成了内存泄漏问题。

我们先举个例子来看看内存泄漏产生的过程及结果

内存泄漏小例子

布局很简单,就是一个按钮和一个TextView

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/numTv"
        android:text="数值"
        android:padding="10dp"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn"
        app:layout_constraintTop_toBottomOf="@id/numTv"
        android:layout_width="match_parent"
        android:layout_marginTop="10dp"
        android:layout_height="wrap_content"
        android:text="点击" />

</android.support.constraint.ConstraintLayout>

就长这样

添加rxjava依赖,这里我是用kotlin写的demo,所以依赖的是rxkotlin,使用java的直接依赖rxjava的sdk即可。

 /*RxJava相关依赖*/
    implementation "io.reactivex.rxjava2:rxkotlin:2.2.0"
    implementation "io.reactivex.rxjava2:rxandroid:2.0.2

下面我们来看看Activity的代码,kotlin写的,也很简单。这里扯点题外话,kotlin写起来真的很爽,不需要findviewbyid,空安全,类型推断,扩展函数等特性用起来真的很爽,在实际项目中能减少很多代码,值得一试。

package com.yzq.autodisposedemo

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.View
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity(www.yongshiyule178.com), View.OnClickListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn.setOnClickListener(this)
    }
    override fun onClick(view: View?) {
        when (view!!.id) {
            R.id.btn ->
                doSomting(www.thd178.com)
        }
    }
    private fun doSomting() {
        Observable.interval(1, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())//在主线程处理结果
                .subscribeOn(Schedulers.io(www.baohuayule.com))//工作线程处理逻辑
                .subscribe(Consumer {
                    Log.i("rxjava发射的数据",it.toString())
                    numTv.text = it.toString(www.mhylpt.com)

                })
    }

    override fun onDestroy(www.taohuayuan178.com ) {
        super.onDestroy()

    

我们在点击按钮后,RxJava开始每隔一秒就发一个数值给我们,我们将其更新到textview上。但是当我们关闭应用时,rxjava仍然在继续发送数据,并且还持有MainActivity的实例,这就导致了MainActivity 无法被回收,造成了内存泄漏。 
如下图所示,我们在多次点击按钮后,然后关闭应用,再点击内存回收按钮后发现内存中的MainActivity实例并没有被销毁。内存分析具体看下图

在实际项目中我们经常有这种需求,比如网络请求一个接口,然后更新ui等。这些操作都可能产生内存泄漏。下面我们来看看解决办法。


如何解决RxJava内存泄漏

1.在ondestory中手动切断连接(不推荐) 
这种方法是比较原始的方法,在onSubscribe我们将Disposable保存起来,在onDestory中调用disposable.dispose()取消订阅

如果在实际开发中使用这种方法,我们需要手动的去维护所有RxJava产生的Disposable,费时费力。

package com.yzq.autodisposedemo

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.View
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity(), View.OnClickListener {

    lateinit var disposable: Disposable

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn.setOnClickListener(this)
    }

    override fun onClick(view: View?) {
        when (view!!.id) {
            R.id.btn ->
                doSomting()
        }
    }

    private fun doSomting() {
        Observable.interval(1, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())//在主线程处理结果
                .subscribeOn(Schedulers.io())//工作线程处理逻辑
                .subscribe(object : Observer<Long> {

                    override fun onSubscribe(d: Disposable) {
                        disposable = d

                    }

                    override fun onNext(t: Long) {
                        Log.i("rxjava发射的数据", t.toString())
                        numTv.text = t.toString()
                    }

                    override fun onError(e: Throwable) {
                    }

                    override fun onComplete() {
                    }

                })
    }

    override fun onDestroy() {
        super.onDestroy()
        disposable.dispose()//取消绑定
        Log.i("MainActivity", "onDestroy")

2.使用RxLifecycle自动解绑(不推荐) 
首先 RxLifecycle Github地址。 
在使用AutoDispose之前,我一直使用的是RxLifecycle去解决RxJava的内存泄漏问题。 
使用方法很简单。具体使用方法我这里就不介绍了,很简单,可以直接看Github上的文档。 
大体使用方法如下

    Observable.interval(1, TimeUnit.SECONDS)
                .doOnUnsubscribe { Log.i(TAG, "Unsubscribing subscription from onCreate()") }
                .bindUntilEvent(this, ActivityEvent.PAUSE)
                .subscribe { num -> Log.i(TAG, "Started in onCreate(), running until onPause(): " + num!!) }

我们可以直接使用bindUntilEvent(this, ActivityEvent.PAUSE)来实现自动解绑的功能,并且可以指定在哪个生命周期去自动解绑,但是前提条件是我们的Activity必须要继承RxLifecycle提供的RxAppCompatActivity,同样的我们的Fragment必须要继承RxLifecycle提供的RxFragment 
在Java中,类都是单继承的,我们的Activity如果需要继承别的类,那么我们就必须多写一个Activity基类供下面的Activity去继承。这显然是不优雅的实现方式。 
如果说你的Activity或Fragment不需要继承其他的Activity或Fragment,那么使用RxLifecycle也没有什么不妥。

3.使用AutoDispose优雅的实现RxJava自动解绑

首先是 AutoDispose Github地址。 
AutoDispose是uber的一个开源库。

我们先来看看使用方法:

在Java中使用

  Observable.interval(1, TimeUnit.SECONDS)
        .doOnDispose(new Action() {
          @Override public void run() throws Exception {
            Log.i(TAG, "Disposing subscription from onResume() with untilEvent ON_DESTROY");
          }
        })
        .as(AutoDispose.<Long>autoDisposable(
            AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY)))//OnDestory时自动解绑
        .subscribe(new Consumer<Long>() {
          @Override public void accept(Long num) throws Exception {
            Log.i(TAG, "Started in onResume(), running until in onDestroy(): " + num);
       

在Kotlin中使用

 Observable.interval(1, TimeUnit.SECONDS)
        .doOnDispose {
          Log.i(TAG, "Disposing subscription from onResume() with untilEvent ON_DESTROY")
        }
        .autoDisposable(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))//OnDestory时自动解绑
        .subscribeBy { num -> Log.i(TAG, "Started in onResume(), running until in onDestroy(): $num") 

可以看到,使用方法跟RxLifecycle很像,我们只需要调用一下 
autoDisposable(AndroidLifecycleScopeProvider.from(this,Lifecycle.Event.ON_DESTROY)) 即可(这里是kotlin的写法,java的可以看官方的demo) 
首先我们来看看 
AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY)中的this是什么。

通过源码我们可以看到这个this实际上是LifecycleOwner这个接口,也就是说只要是在实现这个接口的类中我们就可以直接使用 
autoDisposable(AndroidLifecycleScopeProvider.from(this,Lifecycle.Event.ON_DESTROY)) 
进行自动解绑。

AutoDispose比RxLifecycle好的地方在于它不需要你的Activity或Fragment继承指定的类。只要你的Activity或Fragment的父类实现了LifecycleOwner这个接口即可。 
通过源码发现,support.v7包中的AppCompatActivity最终继承自SupportActivity,SupportActivity实现了LifecycleOwner接口。 
support.v4包中的Fragment也实现了LifecycleOwner接口。 
而我们目前的项目中为了保证兼容性,都是要依赖Android Support v7这个包的。这样一来我们就可以优雅的通过AutoDispose解决RxJava产生的内存泄漏问题了

AutoDispose代替RxLifecycle优雅的解决RxJava内存泄漏问题的更多相关文章

  1. Android性能优化之利用Rxlifecycle解决RxJava内存泄漏

    前言: 其实RxJava引起的内存泄漏是我无意中发现了,本来是想了解Retrofit与RxJava相结合中是如何通过适配器模式解决的,结果却发现了RxJava是会引起内存泄漏的,所有想着查找一下资料学 ...

  2. 利用NSProxy解决NSTimer内存泄漏问题

    之前写过一篇利用RunTime解决由NSTimer导致的内存泄漏的文章,最近和同事讨论觉得这样写有点复杂,然后发现有NSProxy这么好用的根类,根类,根类,没错NSProxy与NSObject一样是 ...

  3. 使用Xcode和Instruments调试解决iOS内存泄漏

    尽管iOS 5.0加入版本号之后ARC机制,由于相互引用关系是复杂的.内存泄漏可能仍然存在.于是,懂原理是非常重要的. 这里讲述在没有ARC的情况下,怎样使用Instruments来查找程序中的内存泄 ...

  4. 使用pidof/kill组合命令,变相解决mediaserver内存泄漏【转】

    本文转载自:https://blog.csdn.net/lj402159806/article/details/78950384 在5.1系统下mediaserver有内存泄漏的问题,原因在于使用ca ...

  5. Rxlifecycle使用详解,解决RxJava内存泄露问题

    http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1122/3711.html

  6. 一步步调试解决iOS内存泄漏

      虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还是可能存在.所以了解原理很重要. 这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄 ...

  7. 调试解决iOS内存泄漏

    这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄露,以及NSZombieEnabled设置的使用. 本文假设你已经比较熟悉Obj-C的内存管理机制. 实验的开发环境:X ...

  8. 第四十三篇、利用NSProxy解决NSTimer内存泄漏问题

    问题描述: 用NSTimer来实现每隔一定时间执行制定的任务,例如最常见的广告轮播图.如果我们在 timerWithTimeInterval:1 target:self 中指定target为当前控制器 ...

  9. Android性能优化之利用LeakCanary检测内存泄漏及解决办法

    前言: 最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到性能优化中如何避免内存泄漏问题时,很少有人全面的回答上来. ...

随机推荐

  1. squid如何屏蔽User-Agent为空的请求

    搞定了,反过来就行了acl has_user_agent browser ^ http_access deny !has_user_agent

  2. Mybatis-延迟加载和缓存

    延迟加载 概念: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据.延迟加载也称懒加载. 好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表 ...

  3. python_52_函数返回值2

    def test1(x,y): print(x,y) test1(1,2)#位置参数调用,按顺序来,与形参一一对应 test1(y=1,x=2)#输出为2 1,不是1 2.关键字参数调用按关键字,不按 ...

  4. 8--oop

    oop-Python面向对象 Python的面向对象 面向对象编程 基础 共有私有 继承 组合,Mixin 魔法函数 魔法函数概述 构造类魔法函数 运算类魔法函数 1.面向对象概述(ObjectOri ...

  5. 牛客小白月赛1 G あなたの蛙は旅⽴っています【图存储】【DP】

    题目链接:https://www.nowcoder.com/acm/contest/85/G 思路: DP 空间可以优化成一维的, 用一维数组的 0 号单元保存左斜对角的值即可. 存图这里真不好理解 ...

  6. cf550D. Regular Bridge(构造)

    题意 给出一个$k$,构造一个无向图,使得每个点的度数为$k$,且存在一个桥 Sol 神仙题 一篇写的非常好的博客:http://www.cnblogs.com/mangoyang/p/9302269 ...

  7. ABAP读取长文本的方法

    SAP中所有的项目文本都存在以下两张数据表中: 1. STXH  抬头项目文本 透明表 2. STXL  明细项目文本   透明表 长文本读取方法 首先在STXH和STXL中根据OBJECT NAME ...

  8. 爬虫学习(八)——带cookie的网页进行爬取

    # 前提:# # 通常,很多网站需要登录才能进行浏览,所以在爬取这些网站时,也需要进行登录,并拿取登录时的cookie# # 登录网页,服务器会给客户端一个牌子cookie# # 访问登录页面时,带着 ...

  9. 【Ecshop】v2.7.3模板变量标签改进

    改进代码后虽可解决大多数函数参数的问题,但也同样产生了参数问题:ecshop模板函数参数有部分没有被引号包裹,所以正则并不能匹配到,要修改为引号包裹,那是个大工程. 为了使ecshop模板支持date ...

  10. thinkphp 3.2.3 程序执行时序图