史上超详细的RxJava2使用和RxLifecycle生命周期管理,解决RxJava内存泄漏

这篇文章基于RxJava2.0

RxJava是什么?
官网说RxJava是一个可观测的序列来组成异步的额,基于事件的库,简单来说,它就是一个实现异步的库,可以代替Android的API如AsyncTask ,Handler等等。

RxJava为什么好?
RxJava其实就是提供一套异步编程的API,这套API是基于观察者模式的,而且是链式调用的,所以使用RxJava编写的代码逻辑会非常简洁。

观察者模式:
定义:对象间一种一对多的关系,使得每当一个对象改变,则所有依赖于它的对象都会得到通知并被自动更新。
作用:解耦,UI层与具体的业务逻辑解耦。

官方支持时间?
官方支持更新到2020年12月31日,没关系的,还有Rxjava3。

使用场景?
可以进行数据库的写入,大图片的载入,文件压缩和解压等各种需要放在后台工作的耗时操作,都可以使用RxJava来实现,可以使用RxJava来实现响应式编程。

如何使用?

添加依赖:

//RxJava2
implementation 'io.reactivex.rxjava2:rxjava:2.2.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'

通过RxJava最简单的一个例子引出它的三个基本元素:观察者,被观察者和订阅。

被观察者

Observable observable = Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                emitter.onNext("Hello");//发送事件时,观察者会回调onNext方法
                emitter.onComplete();//这个顺序不能颠倒,如果onNext方法放在最后,onNext就不会执行了
            }
}).subscribeOn(Schedulers.io())//实际项目中网络请求在io线程
  .observeOn(AndroidSchedulers.mainThread());//这里意为观察者在主线程更新UI

create是RXJava中最基本的操作符,

观察者

Observer<String> observer = new Observer<String>() {
    @Override
    public void onSubscribe(Disposable d) {//这个方法在订阅前就会被调用
        Log.i("log", "onSubscribe->" + d);
        //d.dispose();//取消发射事件
    }

    @Override
    public void onNext(String s) {
         Log.i("log", "onNext->" + s);
    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onComplete() {

    }
};

订阅观察者:

observable.subscribe(observer);

被观察者订阅观察者后,observable中的方法会被立刻回调 。

除了Observable,还有其它4个被观察者可以操作,一共5种



 

接下来看看其它4种被观察者是如何使用的

被观察者(背压)Flowable

Flowable flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
     @Override
     public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
         for (int i = 0; i < 1000000000; i++) {
              emitter.onNext(i);
              //这里还是发射了20条数据
              Log.d("log", "subscribe: " + i);
         }
        emitter.onComplete();
     }
}, BackpressureStrategy.BUFFER).subscribeOn(Schedulers.io());         

观察者(背压)

Subscriber<Integer> subscriber = new Subscriber<Integer>() {
    @Override
    public void onSubscribe(Subscription s) {
         Log.d("log", "onSubscribe");
         //这里体现的是响应式拉取
         //s.request(Long.MAX_VALUE);//指定下游(观察者)接收数据的最大值
         s.request(10);
    }

    @Override
    public void onNext(Integer integer) {
         //拉取10条数据
         Log.d("log", "onNext: " + integer);
    }

    @Override
    public void onError(Throwable t) {
    }

    @Override
    public void onComplete() {
       Log.d("log", "onComplete");
    }
};

被观察者(Single)

Single.create(new SingleOnSubscribe<String>() {
     @Override
     public void subscribe(SingleEmitter<String> emitter) throws Exception {
           emitter.onSuccess("消息");
     }
}).subscribe(new SingleObserver<String>() {
    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public void onSuccess(String s) {

    }

    @Override
    public void onError(Throwable e) {

    }
});

被观察者(Completable)

Completable.create(new CompletableOnSubscribe() {
    @Override
    public void subscribe(CompletableEmitter emitter) throws Exception {
        emitter.onComplete();
        emitter.onError(new Exception());
    }
});

被观察者(Maybe)

Maybe.create(new MaybeOnSubscribe<String>() {
     @Override
     public void subscribe(MaybeEmitter<String> emitter) throws Exception {
         emitter.onSuccess("消息");
         emitter.onComplete();
         emitter.onError(new Exception());
     }
});

操作符:操作符包括创建操作符,转换操作符,组合操作符,功能操作符,过滤操作符,条件操作符。

举个just的例子
just和creat一样,也是创建,只是最多不能超过10个参数

   //可以传入多个参数
   //Observable.just("a",1,2,"b").subscribe(new Observer<Object>() {
   //也可以传入一个方法
   Observable.just(getNumber()).subscribe(new Observer<Object>() {
       @Override
       public void onSubscribe(Disposable d) {

       }

       @Override
       public void onNext(Object o) {
            Log.i("log", "" + o.toString());
       }

       @Override
       public void onError(Throwable e) {

       }

       @Override
       public void onComplete() {

       }
   });

public int getNumber() {
    return 1;
}

组合操作符

//组合操作符
Observable.concat(Observable.just(1, 2),
                Observable.just(5, 6),
                Observable.just(3, 4),
                Observable.just(7, 8)).subscribe(new Observer<Integer>() {
      @Override
      public void onSubscribe(Disposable d) {

      }

      @Override
      public void onNext(Integer integer) {
           Log.i("log", "" + integer);
      }

      @Override
      public void onError(Throwable e) {

      }

      @Override
      public void onComplete() {

      }
});

输出结果为:1,2,5,6,3,4,7,8

过滤操作符

Observable.just(1, 2)
                .subscribeOn(Schedulers.io())
                .filter(new Predicate<Integer>() {
                    @Override
                    public boolean test(Integer integer) throws Exception {
                        return integer < 2;//发送数字小于2的消息
                    }
                })
                .subscribe(new Observer<Integer>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.i("log", "" + integer);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });

条件操作符

RxJava的线程切换是如何实现的?
来了解下线程切换的代码实现

Observable observable = Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                emitter.onNext("Hello");
                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.io())//网络请求在io线程
                .observeOn(AndroidSchedulers.mainThread());//这里意为观察者在主线程更新UI

RxJava的线程切换是通过Scheduler(线程调度器)来实现的,Scheduler的作用是简化了异步操作。

subscribeOn:通过接收一个Schedule参数,来指定对数据的处理运行在特定的调度器Schedule上,若多次设定,则只有一次起作用。

observeOn:接收一个Schedule参数,来指定下游(RxJava官网把观察者称为下游)操作运行在特定的线程调度器Schedule上,若多次设定,每次均起作用。

Schedule的种类如下:

1)Schedules.io()
用于IO密集型的操作,例如读写SD卡文件,查询数据库,访问网络等,具有线程缓存机制,在此调度器接收到任务后,先检查线程缓存池中,是否有空闲的线程,如果有,则复用,如果没有则创建新的线程,并加入到线程池中,如果每次都没有空闲线程使用,可以无上限的创建新线程。
2)Schedulers.newThread( )
在每执行一个任务时创建一个新的线程,不具有线程缓存机制,因为创建一个新的线程比复用一个线程更耗时耗力,虽然使用Schedulers.io( )的地方,都可以使用Schedulers.newThread( ),但是,Schedulers.newThread( )的效率没有Schedulers.io( )高。
3)Schedulers.computation()
用于CPU 密集型计算任务,即不会被 I/O 等操作限制性能的耗时操作,例如xml,json文件的解析,Bitmap图片的压缩取样等,具有固定的线程池,大小为CPU的核数。不可以用于I/O操作,因为I/O操作的等待时间会浪费CPU。
4)Schedulers.trampoline()
在当前线程立即执行任务,如果当前线程有任务在执行,则会将其暂停,等插入进来的任务执行完之后,再将未完成的任务接着执行。
5)Schedulers.single()
拥有一个线程单例,所有的任务都在这一个线程中执行,当此线程中有任务执行时,其他任务将会按照先进先出的顺序依次执行。
6)Scheduler.from(@NonNull Executor executor)
指定一个线程调度器,由此调度器来控制任务的执行策略。
7)AndroidSchedulers.mainThread()
在Android UI线程中执行任务,为Android开发定制。

RxJava中的背压

背压出现的原因:
当上下游在不同的线程中,通过Observable发射,处理,响应数据流时,如果上游发射数据的速度快于下游接收处理数据的速度,这样对于那些没来得及处理的数据就会造成积压,这些数据既不会丢失,也不会被垃圾回收机制回收,而是存放在一个异步缓存池中,如果缓存池中的数据一直得不到处理,越积越多,最后就会造成内存溢出,这便是响应式编程中的背压(backpressure)问题

背压策略的解决思路:
利用响应式拉取,响应式拉取是观察者主动去被观察者那里拉取事件,而被观察者则是被动等待通知再发射事件。

BackpressureStrategy背压策略:
1)MISSING
MissingEmitter:在此策略下,通过Create方法创建的Flowable相当于没有指定背压策略,不会对通过onNext发射的数据做缓存或丢弃处理,需要下游通过背压操作符。

2)ERROR
ErrorAsyncEmitter:在此策略下,如果放入Flowable的异步缓存池中的数据超限了,则会抛出MissingBackpressureException异常。

3)BUFFER
BufferAsyncEmitter:部维护了一个缓存池SpscLinkedArrayQueue,其大小不限,此策略下,如果Flowable默认的异步缓存池满了,会通过此缓存池暂存数据,它与Observable的异步缓存池一样,可以无限制向里添加数据,不会抛出MissingBackpressureException异常,但会导致OOM

4)DROP
DropAsyncEmitter:在此策略下,如果Flowable的异步缓存池满了,会丢掉上游发送的数据。

5)LATEST
LatestAsyncEmitter:与Drop策略一样,如果缓存池满了,会丢掉将要放入缓存池中的数据,不同的是,不管缓存池的状态如何,LATEST都会将最后一条数据强行放入缓存池中,来保证观察者在接收到完成通知之前,能够接收到Flowable最新发射的一条数据

RxJava的生命周期

为什么要关注RxJava的生命周期?
来看一个小例子
MainActivity.java

Flowable flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
    @Override
    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
        for (int i = 0; i < 1000000000; i++) {
            emitter.onNext(i);
            Log.d("log", "subscribe: " + i);
        }
        emitter.onComplete();
    }
}, BackpressureStrategy.BUFFER)
     .subscribeOn(Schedulers.io());

比如我们这里有一个按钮,从MainActivity跳转到MainActivity2中,当跳到MainActivity2的时候,可以通过log看到日志中还是输出打印,这样是很不友好的,更重要的是会造成内存溢出,所以这就是为什么要对RxJava的生命周期进行管理了。

RxLifecycle的github地址:

https://github.com/trello/RxLifecycle

我这里用的是3.0的Rxlifecycle,需要依赖依androidx,Rxlifecycle和androidx的配置如下:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'

    //RxJava2
    implementation 'io.reactivex.rxjava2:rxjava:2.2.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'

    //3.0这个是基于androidx的
    implementation 'com.trello.rxlifecycle3:rxlifecycle:3.0.0'
    implementation 'com.trello.rxlifecycle3:rxlifecycle-android:3.0.0'
    implementation 'com.trello.rxlifecycle3:rxlifecycle-components:3.0.0'
}

然后让我们的Activity继承RxAppCompatActivity即可。

Observable.interval(1, TimeUnit.SECONDS).doOnDispose(new Action() {
            @Override
            public void run() throws Exception {

            }
        })
                .compose(this.<Long>bindUntilEvent(ActivityEvent.PAUSE));

这里我设置的是在pause的时候取消订阅,需要结合rxlifecycle使用,当然也可以设置别的状态来管理,比如onPause,onDestroy等。



作者:凌烟醉卧
链接:https://www.jianshu.com/p/99cddc8c338c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[2\]:上面我们介绍了了关于RxJava的基本的概念和使用方式,在下面的文章中我们会按照以上定义的顺序从API的角度来讲解以下RxJava各个模块的使用方法。引用\[3\]:retry使用了一种错误重试机制,它可以在出现错误的时候进行重试,我们可以通过参数指定重试机制的条件。以下面的程序为例,这里我们设置了当出现错误的时候会进行2次重试,因此,第一次的时候出现错误会调用onNext,重试2次又会调用2次onNext,第二次重试的时候因为重试又出现了错误,因此此时会触发onError方法。也就是说,下面这段代码会触发onNext3次,触发onError()1次。 问题: rxjava2 defer是什么意思? 回答: 在RxJava2中,defer是一个非常有用的操作符。它允许我们在每次订阅时创建一个新的Observable,以便能够动态地生成Observable。defer操作符接受一个Observable的工厂函数作为参数,当有观察者订阅时,它会调用这个工厂函数创建一个新的Observable。这样,每次订阅时都会得到一个全新的Observable实例,可以确保每个订阅者都能够获取到最新的数据。defer操作符的使用场景包括延迟创建Observable、动态地生成Observable等。 #### 引用[.reference_title] - *1* *2* *3* [RxJava 系列-1:一篇的比较全面的 RxJava2 方法总结](https://blog.csdn.net/weixin_34411563/article/details/87985915)[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 ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值