rxjava2
如果生产者的速度大于消费者的速度,就会产生Backpressure问题。即异步情况下,Backpressure问题才会存在。
由于上游通过Observable发射数据的速度大于下游通过Consumer接收处理数据的速度,而且上下游分别运行在不同的线程中,下游对数据的接收处理不会堵塞上游对数据的发射,造成上游数据积压,内存不断增加,最后便会导致内存溢出。
Observable.create(new ObservableOnSubscribe<Integer>() { // 第一步:初始化Observable
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Exception {
Log.e(TAG, "Observable emit 1" + "\n");
e.onNext(1);
Log.e(TAG, "Observable emit 2" + "\n");
e.onNext(2);
Log.e(TAG, "Observable emit 3" + "\n");
e.onNext(3);
e.onComplete();
Log.e(TAG, "Observable emit 4" + "\n" );
e.onNext(4);
}
}).subscribe(new Observer<Integer>() { // 第三步:订阅
// 第二步:初始化Observer
private int i;
private Disposable mDisposable;
@Override
public void onSubscribe(@NonNull Disposable d) {
mDisposable = d;
}
@Override
public void onNext(@NonNull Integer integer) {
i++;
if (i == 2) {
// 在RxJava 2.x 中,新增的Disposable可以做到切断的操作,让Observer观察者不再接收上游事件
mDisposable.dispose();
}
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "onError : value : " + e.getMessage() + "\n" );
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete" + "\n" );
}
});
Schedulers.io()
代表io操作的线程, 通常用于网络,读写文件等io密集型的操作;Schedulers.computation()
代表CPU计算密集型的操作, 例如需要大量计算的操作;Schedulers.newThread()
代表一个常规的新线程;AndroidSchedulers.mainThread()
代表Android的主线程
1、非背压
/**
* 非背压
* Observable对应Observer
*/
private void createObservable() {
//被观察者
Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
e.onNext("This");
e.onNext("is");
e.onNext("RxJava");
e.onComplete();
}
});
//观察者
Observer<String> observer = new Observer<String>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
@Override
public void onNext(String s) {
Log.i(TAG, "onNext: " + s);
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: " + e.getLocalizedMessage());
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete");
//取消订阅
if (!disposable.isDisposed()) {
disposable.dispose();
}
}
};
observable.subscribe(observer);
}
2、背压
/**
* 背压(在异步过程中,由于被观察者发射数据过快,而观察者处理数据不及时,
* 导致内存里堆积了太多数据,从而OOM,可以选择不同的策略处理该问题)
* Flowable对应subscriber
*/
private void createFlowable() {
Flowable<String> flowable = Flowable.create(new FlowableOnSubscribe<String>() {
@Override
public void subscribe(FlowableEmitter<String> e) throws Exception {
if (!e.isCancelled()) {
e.onNext("This");
e.onNext("is");
e.onNext("RxJava");
e.onComplete();
}
}
//抛弃策略
}, BackpressureStrategy.DROP);
Subscriber<String> subscriber = new Subscriber<String>() {
Subscription subscription;
@Override
public void onSubscribe(Subscription s) {
subscription = s;
//请求一个数据
subscription.request(1);
}
@Override
public void onNext(String s) {
Log.i(TAG, "onNext: " + s);
//处理完后,再请求一个数据
subscription.request(1);
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: " + e.getLocalizedMessage());
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete");
//取消订阅
subscription.cancel();
}
};
flowable.subscribe(subscriber);
}
关于有无返回值,有返回值便于管理
subscribe(Observer);无返回值
subscribe(Consumer);有返回值
subscribeWith(new ResourceObserver)有返回值
如图所示,通过 setOnClickListener()
方法,Button
持有 OnClickListener
的引用(这一过程没有在图上画出);当用户点击时,Button
自动调用 OnClickListener
的 onClick()
方法。另外,如果把这张图中的概念抽象出来(Button
-> 被观察者、OnClickListener
-> 观察者、setOnClickListener()
-> 订阅,onClick()
-> 事件),就由专用的观察者模式(例如只用于监听控件点击)转变成了通用的观察者模式。如下图:
而 RxJava 作为一个工具库,使用的就是通用形式的观察者模式。
RxJava 的观察者模式
RxJava 有四个基本概念:Observable
(被观察者)、 Observer
(观察者)、 subscribe
(订阅)、事件。Observable
和 Observer
通过 subscribe()
方法实现订阅关系,从而 Observable
可以在需要的时候发出事件来通知 Observer
。
rxjava原理:观察者模式+handler机制切换线程
rxjava线程切换
-
只有第一subscribeOn() 起作用(所以多个 subscribeOn() 没意义),
doOnSubscribe受下面的subscribeOn控制线程
-
这个 subscribeOn() 控制从流程开始的第一个操作,直到遇到第一个 observeOn()
-
observeOn() 可以使用多次,每个 observeOn() 将导致一次线程切换(),这次切换开始于这次 observeOn() 的下一个操作
-
不论是 subscribeOn() 还是 observeOn(),每次线程切换如果不受到下一个 observeOn() 的干预,线程将不再改变,不会自动切换到其他线程
just,from是做集合遍历的,
flatMap是做类型转换的,并且是一对多的类型转换,
map也是类型转换的是一对一的转换,
flatMap和map的区别 map String,String -> String
flatMap String,Observable<String> -> String
/* -------------------------------map-------------------------------------------------*/
Observable.just("a", "b", "c")
//使用map进行转换,参数1:转换前的类型,参数2:转换后的类型
.map(new Func1<String, String>() {
@Override
public String call(String i) {
String name = i;
Log.i("map", "map--1----" + i);
return name;
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.i("map", "map--2----" + s);
}
});
11-23 10:24:31.350 12902-12902/com.example.seele.retrofit I/tag: map--1----a
11-23 10:24:31.350 12902-12902/com.example.seele.retrofit I/tag: map--2----a
11-23 10:24:31.350 12902-12902/com.example.seele.retrofit I/tag: map--1----b
11-23 10:24:31.350 12902-12902/com.example.seele.retrofit I/tag: map--2----b
11-23 10:24:31.350 12902-12902/com.example.seele.retrofit I/tag: map--1----c
11-23 10:24:31.350 12902-12902/com.example.seele.retrofit I/tag: map--2----c
/* -------------------------------flatMap-------------------------------------------------*/
Observable.just("a", "b", "c").flatMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String s) {
Log.i("flatMap", "map--1----" + s);
return Observable.just(s + "!!!", "d");
}
}).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.i("flatMap", "map--2----" + s);
}
});
11-23 10:53:51.320 14442-14527/? I/tag: map--1----a
11-23 10:53:51.320 14442-14527/? I/tag: map--1----b
11-23 10:53:51.320 14442-14527/? I/tag: map--1----c
11-23 10:53:51.340 14442-14442/? I/tag: map--2----a!!!
11-23 10:53:51.340 14442-14442/? I/tag: map--2----b!!!
11-23 10:53:51.340 14442-14442/? I/tag: map--2----c!!!
map是分个返回,flatMap是集中返回
filter是做筛选的过滤的,
Observable.just(3, 4, 5, 6).filter(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
return integer > 4;
}
}).subscribe(item -> Log.d("JG", item.toString())); //5,6
concat: 按顺序连接多个Observables。需要注意的是Observable.concat(a,b)
等价于a.concatWith(b)
。串行
Observable<Integer> observable1 = Observable.just(1, 2, 3, 4);
Observable<Integer> observable2 = Observable.just(4, 5, 6);
Observable.concat(observable1, observable2).subscribe(item -> Log.d("JG", item.toString()));//1,2,3,4,4,5,6
startWith: 在数据序列的开头增加一项数据。startWith
的内部也是调用了concat
Observable.just(1, 2, 3, 4, 5)
.startWith(6, 7, 8)
.subscribe(item -> Log.d("JG", item.toString()));//6,7,8,1,2,3,4,5
merge和concat对照,是并行的
interval返回一个每隔指定的时间间隔就发射一个序列号的 Observable 对象,可用来做倒计时等操作。
例子: 每隔 1 s 发送一个序列号,序列号从 0 开始,每次累加 1。
Observable<Long> observable = Observable.interval(1, TimeUnit.SECONDS);
take只发射前面指定数量或指定时间内的元素。Observable.just(-1, -2,0,1,2).take(3); // 只发射前三条数据
Observable.just(3,4,5,6)
.take(3)//发射前三个数据项
.take(100, TimeUnit.MILLISECONDS)//发射100ms内的数据
默认情况下, doOnSubscribe() 是和被观察者在一个线程(订阅者线程),并且是第一个执行的,可以执行比如:网络访问的加载框,多个doOnSubscribe从下向上顺序执行, 在doOnSubscribe紧跟后面使用subscribeOn() 控制执行的线程
compose
好处在于: 1.实现一系列操作符的复用 2.不破坏链式调用的结构
我们知道,compose和flatMap都是以Observable<T>作为输入,Observable<R>作为输出,那么两者有什么区别呢
1.compose() 是唯一一个能从流中获取原生Observable 的方法,因此,影响整个流的操作符(像subscribeOn()和observeOn())需要使用compose(),相对的,如果你在flatMap()中使用subscribeOn()/observeOn(),它只影响你创建的flatMap()中的Observable,而不是整个流。
2.当你创建一个Observable流并且内联了一堆操作符以后,compose()会立即执行,flatMap()则是在onNext()被调用以后才会执行,换句话说,flatMap()转换的是每个项目,而compose()转换的是整个流。
3.flatMap()一定是低效率的,因为他每次调用onNext()之后都需要创建一个新的Observable,compose()是操作在整个流上的。
rxjava操作符大全https://www.jianshu.com/p/3fdd9ddb534b
- 倒计时的实现
- final int count = 30;
- Observable.interval(0, 1, TimeUnit.SECONDS)//设置0延迟,每隔一秒发送一条数据
- .take(count+1) //设置循环11次
- .map(new Func1<Long, Long>() {
- @Override
- public Long call(Long aLong) {
- return count-aLong; //
- }
- })
- .doOnSubscribe(new Action0() {
- @Override
- public void call() {
- mSend.setEnabled(false);//在发送数据的时候设置为不能点击
- mSend.setBackgroundColor(Color.GRAY);//背景色设为灰色
- }
- })
- .observeOn(AndroidSchedulers.mainThread())//操作UI主要在UI线程
- .subscribe(new Observer<Long>() {
- @Override
- public void onCompleted() {
- Log.d(TAG, "onCompleted: ");
- mSend.setText("获取验证码");//数据发送完后设置为原来的文字
- mSend.setTextColor(Color.BLACK);
- mSend.setBackgroundColor(Color.parseColor("#f97e7e"));//数据发送完后设置为原来背景色
- }
- @Override
- public void onError(Throwable e) {
- e.printStackTrace();
- }
- @Override
- public void onNext(Long aLong) { //接受到一条就是会操作一次UI
- Log.d(TAG, "onNext: "+aLong);
- mSend.setText("剩余时间"+aLong+"秒");
- mSend.setEnabled(true);
- mSend.setTextColor(Color.WHITE);
- }
- });
异步的实现
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
//可以做各种复杂的操作,然后进行回调
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
subscriber.onNext("jack : 我是最后的执行结果");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
subscriber.onNext("jack : 我被计算出来了");
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe( //此行为订阅,只有真正的被订阅,整个流程才算生效
new Observer<String>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, e.toString());
}
@Override
public void onNext(String s) {
Log.d(TAG, s);
}
});
线程
倘若我们要进行一些数据的耗时操作,而且线程切换自由
在RxJava 中,Scheduler ——调度器,相当于线程控制器,
subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。
observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。
Schedulers.immediate(): 直接在当前线程运行,相当于不指定线程。这是默认的 Scheduler。
Schedulers.newThread(): 总是启用新线程,并在新线程执行操作。
Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。
Schedulers.computation(): 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。
new Thread() {
@Override
public void run() {
super.run();
for (File folder : folders) {
File[] files = folder.listFiles();
for (File file : files) {
if (file.getName().endsWith(".png")) {
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}
}.start();
而如果使用 RxJava ,实现方式是这样的:
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
});
代码块可以看出操作符是有先后顺序的,
just,from是做集合遍历的,
flatMap是做类型转换的,
并且是一对多的类型转换,
map也是类型转换的是一对一的转换,
filter是做筛选的过滤的,
interval返回一个每隔指定的时间间隔就发射一个序列号的 Observable 对象,可用来做倒计时等操作。
例子: 每隔 1 s 发送一个序列号,序列号从 0 开始,每次累加 1。
Observable<Long> observable = Observable.interval(1, TimeUnit.SECONDS);
take只发射前面指定数量或指定时间内的元素。Observable.just(-1, -2,0,1,2).take(3); // 只发射前三条数据
默认情况下, doOnSubscribe() 是和被观察者在一个线程(订阅者线程),并且是第一个执行的,可以执行比如:网络访问的加载框,多个doOnSubscribe从下向上顺序执行, 在doOnSubscribe紧跟后面使用subscribeOn() 控制执行的线程
下载
Observable.just(url)
.filter(new Predicate<String>() { // 过滤 call的map中已经有了,就证明正在下载,则这次不下载
@Override
public boolean test(String s) {
return !downCalls.containsKey(s);
}
})
.map(new Function<String, DownloadInfo>() { // 生成 DownloadInfo
@Override
public DownloadInfo apply(String s) {
return createDownInfo(s);
}
})
.map(new Function<Object, DownloadInfo>() { // 如果已经下载,重新命名
@Override
public DownloadInfo apply(Object o) {
return getRealFileName((DownloadInfo)o);
}
})
.flatMap(new Function<DownloadInfo, ObservableSource<DownloadInfo>>() { // 下载
@Override
public ObservableSource<DownloadInfo> apply(DownloadInfo downloadInfo) {
return Observable.create(new DownloadSubscribe(downloadInfo));
}
})
.observeOn(AndroidSchedulers.mainThread()) // 在主线程中回调
.subscribeOn(Schedulers.io()) // 在子线程中执行
.subscribe(new DownloadObserver()); // 添加观察者,监听下载进度
/**
* Created by lhy
* Date:2018年 09月 12日
* Time:13:50
* —————————————————————————————————————
* About: 观察者
* —————————————————————————————————————
*/
public class DownloadObserver implements Observer<DownloadInfo> {
public Disposable d;//可以用于取消注册的监听者
public DownloadInfo downloadInfo;
@Override
public void onSubscribe(Disposable d) {
this.d = d;
}
@Override
public void onNext(DownloadInfo value) {
this.downloadInfo = value;
downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD);
EventBus.getDefault().post(downloadInfo);
}
@Override
public void onError(Throwable e) {
Log.d("My_Log","onError");
if (DownloadManager.getInstance().getDownloadUrl(downloadInfo.getUrl())){
DownloadManager.getInstance().pauseDownload(downloadInfo.getUrl());
downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_ERROR);
EventBus.getDefault().post(downloadInfo);
}else{
downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_PAUSE);
EventBus.getDefault().post(downloadInfo);
}
}
@Override
public void onComplete() {
Log.d("My_Log","onComplete");
if (downloadInfo != null){
downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_OVER);
EventBus.getDefault().post(downloadInfo);
}
}
}