四时宝库

程序员的知识宝库

AutoDispose代替RxLifecycle优雅的解决RxJava内存泄漏问题

使用过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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

就长这样

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

/*RxJava相关依赖*/

implementation "io.reactivex.rxjava2:rxkotlin:2.2.0"

implementation "io.reactivex.rxjava2:rxandroid:2.0.2"

  • 1
  • 2
  • 3
  • 4
  • 5

下面我们来看看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(), 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()

}

}

private fun doSomting() {

Observable.interval(1, TimeUnit.SECONDS)

.observeOn(AndroidSchedulers.mainThread())//在主线程处理结果

.subscribeOn(Schedulers.io())//工作线程处理逻辑

.subscribe(Consumer {

Log.i("rxjava发射的数据",it.toString())

numTv.text = it.toString()

})

}

override fun onDestroy() {

super.onDestroy()

Log.i("MainActivity","onDestroy")

}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

我们在点击按钮后,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")

}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

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!!) }

  • 1
  • 2
  • 3
  • 4

我们可以直接使用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);

}

});

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在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") }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可以看到,使用方法跟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产生的内存泄漏问题了

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接