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>

就长这样

这里写图片描述

添加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(), 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")
    }
}

我们在点击按钮后,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的一个开源库。注意:0.8.0以后的版本需要先将项目迁移至AndroidX,否则会提示错误

我们先来看看使用方法:

在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接口。AndroidX支持库同样也实现了LifecycleOwner接口。

在这里插入图片描述

注意事项

0.8.0以后的版本需要先将项目迁移至AndroidX,否则AndroidLifecycleScopeProvider.from(this) 这个地方会报红,而且编译会报错:

报错信息大概如下:
None of the following functions can be called with the arguments supplied: public final fun from(p0: (() -> Lifecycle)!, p1: ((Lifecycle.Event) -> Lifecycle.Event!)!): AndroidLifecycleScopeProvider! defined in com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider public final fun from(p0: (() -> Lifecycle)!

如果你还没有打算将项目迁移至AndroidX的话,请不要使用新版本!使用0.8.0这个版本即可

如果你使用的是kotlin,可以看看这篇 Kotlin基于RxJava的扩展方法(超级好用)文章,可以很优雅的实现RxJava的线程切换和自动解绑。


下面是Demo
Kotlin版 Demo

如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Android网络请求的封装可以通过以下步骤实现: 1. 在AndroidManifest.xml文件中添加网络权限,以确保应用程序可以进行网络请求。可以在<manifest>标签内添加以下代码: <uses-permission android:name="android.permission.INTERNET"/> \[1\] 2. 定义一个网络请求回调接口,用于处理请求成功和失败的回调方法。可以创建一个接口,并在接口中定义onSuccess和onFailure方法,如下所示: public interface CallBackLis<T> { void onSuccess(String method, T content); void onFailure(String method, String error); } \[2\] 3. 在Activity或Fragment中调用网络请求方法。可以使用HttpRequest类中的静态方法来发送网络请求,并传入回调接口的实例作为参数。例如: HttpRequest.login(activity, new CallBackLis<具体data类型>() { @Override public void onSuccess(String method, 具体data类型 content) { // 在这里处理请求成功后的逻辑,content为我们需要的数据 } @Override public void onFailure(String method, String error) { // 在这里处理请求失败后的逻辑,error为错误信息 toast(error); } }); \[3\] 通过以上步骤,可以实现Android网络请求的封装,使得网络请求的调用更加简洁和方便。 #### 引用[.reference_title] - *1* [初学Android网络封装](https://blog.csdn.net/weixin_43993331/article/details/122151795)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Android 网络请求封装(Retrofit+OkHttp+RxJava+AutoDispose),解决网络请求引起的内存泄漏](https://blog.csdn.net/u013624014/article/details/122000289)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喻志强(Xeon)

码字不易,鼓励随意。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值