RxJava内存泄漏解决方案之RxLifeCycle

前言:

项目中用到了RxJava在异步线程中执行好事任务,然后在主线中处理结果。这也是RxJava最常用的场景。代码如下,如果直接这样使用可能会造成内存泄漏的,任务要执行5秒,Activity可能都finish了,任务还没执行完。

java 复制代码
Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Throwable {
                //模拟耗时任务
                Thread.sleep(1000 * 5);
                emitter.onNext("hello world");
            }
        }).subscribeOn(Schedulers.io())//线程池执行耗时任务
        .observeOn(AndroidSchedulers.mainThread())//主线程处理结果
        .subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Throwable {
                //结果回调
                Log.i("tag", "accept " + s);
            }
        });

RxJava为什么会内存泄漏。

因为匿名内部类会持有外部类的引用。如果在是 Activity 中使用,则 Activity 则会Consumer的匿名内部类持有,而 Comsumer 的匿名实例则最终 被线程池持有,最终造成了 Activity 的引用无法释放。

启动 ObservableSubscribeOn 的重写方法subscribeActual方法中,项线程池中提交了一个任务:

java 复制代码
@Override
public void subscribeActual(final Observer<? super T> observer) {
    final SubscribeOnObserver<T> parent = new SubscribeOnObserver<>(observer);

    observer.onSubscribe(parent);
    //scheduler 就是 Schedulers.io()
    parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}

如何使用RxLifeCycle解决内存泄漏

解决内存泄漏要么使用 WeakReference ;要么在 onDestory 方法中 调实力isposable的dispose 取消订阅。而使用 RxLifeCycle 则可以自动的在某个生命周期方法中取消订阅。只需如下操作:

Activity 继承 RxAppCompatActivity

scala 复制代码
public class MainActivity extends RxAppCompatActivity {

调用函数 compose(this.bindUntilEvent(ActivityEvent.STOP)) 取消订阅行为 绑定 Activity 生命周期事件就可以了。

less 复制代码
Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Throwable {
                //模拟耗时任务
                Thread.sleep(1000 * 5);
                emitter.onNext("hello world");
            }
        }).subscribeOn(Schedulers.io())//线程池执行耗时任务
        .observeOn(AndroidSchedulers.mainThread())//主线程处理结果】
        .compose(this.bindUntilEvent(ActivityEvent.STOP)) //关键代码
        .subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Throwable {
                //结果回调
                Log.i("tag", "accept " + s);
            }
        });

RxLifeCycle 解决内存泄漏的原理

原理其实上面已经说了,就是指定的生命周期事件中去取消订阅(dispose),下面我们看一下具体实现:

继承 RxAppCompatActivity 类,RxAppCompatActivity 中创建了 BehaviorSubject 对象,并在各个生命周期方法中发射对应的周期事件。

scala 复制代码
public abstract class RxAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {

    private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();

    @Override
    public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
        return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
    }
    
    @Override
    protected void onStop() {
        lifecycleSubject.onNext(ActivityEvent.STOP);
        super.onStop();
    }

再看 RxLifecycle.bindUnitlEvent 函数,我把调用链路收拢一下大概如上,subject 使用 filter 方法只接收传入的事件 上面传入的是 ActivityEvent.STOP。最后返回了 LifecyclerTransformer 的实例。

less 复制代码
@CheckReturnValue
public static <T, R> LifecycleTransformer<T> bindUntilEvent(@Nonnull final Observable<R> lifecycle,
                                                            @Nonnull final R event) {
    
    return new LifecycleTransformer<>(lifecycle.filter(new Predicate<R>() {
        @Override
        public boolean test(R lifecycleEvent) throws Exception {
            return lifecycleEvent.equals(event);
        }
    })
    );
}

再看 compose(:LifecycleTransformer) 函数,实际就是调用 LifecycleTransformer 的 apply 函数。最终调用到 upstream.takeUntil(observable);

其中 upstream 即我们原始的Obserable;observable 是我们RxAppcompatActivity中 lifecycle.filter 生成的 Observable 可以理解为生命周期的被观察者。所以当生命周期事件被触发后,原始的 Observable 会取消订阅(dispose).

takeUntil 函数会返回 ObservableTakeUntil 类的实例,典型的装饰模式。

swift 复制代码
public final <R> Observable<R> compose(@NonNull ObservableTransformer<? super T, ? extends R> composer) {
    return wrap(((ObservableTransformer<T, R>) Objects.requireNonNull(composer, "composer is null")).apply(this));
}

@Override
public ObservableSource<T> apply(Observable<T> upstream) {
    return upstream.takeUntil(observable);
}

最后我们看一下 ObservableTakeUntil 类。同样重写了 subscribeActual 函数。other 属性就是上面的生命周期事件被观察者。 在其被观察者 OtherObserver 的 onNext 函数中,调用 DisposableHelper.dispose(this)。 到此就串联通了😄。

scala 复制代码
public final class ObservableTakeUntil<T, U> extends AbstractObservableWithUpstream<T, T> {

    final ObservableSource<? extends U> other;

    public ObservableTakeUntil(ObservableSource<T> source, ObservableSource<? extends U> other) {
        super(source);
        this.other = other;
    }

    @Override
    public void subscribeActual(Observer<? super T> child) {
        TakeUntilMainObserver<T, U> parent = new TakeUntilMainObserver<>(child);
        child.onSubscribe(parent);
        //注册生命周期事件的监听
        other.subscribe(parent.otherObserver);
        source.subscribe(parent);
    }

    final class OtherObserver extends AtomicReference<Disposable> implements Observer<U> {

            @Override
            public void onNext(U t) {
                //取消订阅
                DisposableHelper.dispose(this);
                otherComplete();
            }
        }
    }

}

总结:

RxLifeCycle 是 RxJava3.0 中的辅助类。再深入理解后原理后值得一用,能帮方便快捷的处理内存泄漏。后面在接口开发模式 MVP, MVVM 做一些二次封装就更好了。

相关推荐
三少爷的鞋1 小时前
AI 时代,我们都将成为通才型开发者:只懂 Android,已经不够了
android
Dovis(誓平步青云)5 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码7 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程7 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程10 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程11 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程12 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程12 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓12 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程12 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer