Rxjava详解
- Rxjava的优点:
- 链式调用,代码调用流程异常清晰 ,代码简洁。
- RxJava和EventBus一样也是基于观察者模式,但是使用的场景确实异步数据流的处理
- RxJava更加强大,利用操作符它可以对发出的消息进行一系列的变换
- 引入依赖:
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
- RxJava到底是什么?
Rxjava的是通过一种扩展的观察者设计模式来实现异步操作,跟AsyncTask和Handler类似,但是比AsyncTask和Handler更加简洁随着程序逻辑变得越来越复杂,它依然能够保持逻辑的简洁。 - 基本实现
(1) 创建观察者Observer,它决定事件触发的时候该有的行为。
private Observer<String> observer = new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
//RxJava 2.0 中新增的,传递参数为Disposable ,Disposable 相当于1.x中的Subscription,调用d.dispose()解除绑定
}
@Override
public void onNext(String value) {
}
@Override
public void onError(Throwable e) {
//在事件处理过程中出异常时,onError()会被触发
}
@Override
public void onComplete() {
//事件队列完结时调用该方法
}
};
(2) 创建被观察者Observable,它决定什么时候以及怎样触发事件
Observable observable = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
e.onNext("A");
e.onNext("B");
//onError和onComplete()只能调用其中一个
e.onComplete();
}
});
Observable的其他创建方式
//just方式创建
Observable<String> observable = Observable.just("Hello","A");
//fromIterable()用于集合数据,fromArray()用于数组数据方式,
List<String> array1=new ArrayList<>();
array1.add("F");
array1.add("G");
Observable.fromIterable(array1).subscribe(observer);
//创建一个按固定时间间隔发射整数序列的Observable,可用作定时器。即按照固定2秒一次调用onNext()方法。
Observable<Long> interval = Observable.interval(2, TimeUnit.SECONDS);
(3) 订阅
//创建了 Observable 和 Observer 之后,再用 subscribe() 方法将它们联结起来
//被观察者订阅观察者,符合流式代码的结构
mObservable.subscribe(observer);
(5) 简洁的写法:
String[] word= new String[]{"hello", "world", "chinses"};
Disposable subscribe = Observable.fromAray(word).subscribe(new Consumer<String>() {
@Override
public void accept(String aLong) throws Exception {
//相当于Observer的onNext()
Log.i(TAG, "accept: " + aLong);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
//相当于Observer的onError()
Log.i(TAG, "onError: ");
}
}, new Action() {
@Override
public void run() throws Exception {
//相当于Observer的onComplete()
Log.i(TAG, "onComplete: ");
}
});
subscribe.dispose();//取消订阅
另外我们需要明白两点:
1.Observable和Observer可以做任何事情
Observable可以是一个数据库查询,Observer用来显示查询结果(传递数据);Observable可以是屏幕上的点击事件,
Observer用来响应点击事件(传递事件);Observable可以是一个网络请求,Observer用来显示请求结果。
2.Observable和Observer是独立于中间的变换过程的。
在Observable和Observer中间可以增减任何数量的操作符。整个系统是高度可组合的,操作数据是一个很简单的过程
- 操作符:
作用:将事件序列中的对象或整个序列进行加工处理,转换层不同的事件或事件序列
- filter() :可以对Observable流程的数据进行一层过滤处理,返回一个新的Observable,filter()返回为false的值将不会发出到Subscriber。
Observable.just("A").map(new Function<String, Integer>() {
@Override
public Integer apply(String s) throws Exception {
return s.length();
}
}).filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) throws Exception {
return integer != 10;
}
}).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.i(TAG, "accept: " + integer);
}
});
- map(): 事件对象的直接变换,一个Observable对象上可以多次使用map操作符
Observable.just("images/logo.png") // 输入类型 String
.map(new Function<String, Bitmap>() {
@Override
public Bitmap apply(String filePath) { // 参数类型 String
return getBitmapFromPath(filePath); // 返回类型 Bitmap
}
}).subscribe(new Consumer<Bitmap>() {
@Override
public void accept(Bitmap bitmap) throws Exception {
showBitmap(bitmap);
}
});
- flatMap():flatMap() 和 map() 有一个相同点:它也是把传入的参数转化之后返回另一个对象,不同的是, flatMap() 中返回的是个 Observable 对象。
//有方法根据输入的字符串返回一个List集合信息
Observable<List<String>> query(String text);
//假如不用flatMap()我们应该这样写:
query("message")
.subscribe(new Function<List<String>>() {
@Override
public void apply(List<String> mLists) {
Observable.fromIterable(mLists)
.subscribe(new Consumer<String>() {
@Override
public void accept(String message) {
log.i(TAG,message);
}
});
}
});
使用flatMap()这样写:
query("Hello, world!")
.flatMap(new Function<List<String>, Observable<String>>() {
@Override
public Observable<String> apply(List<String> urls) {
return Observable.from(urls);
}
})
.subscribe(new Consumer<String>() {
@Override
public void accept(String message) {
log.i(TAG,message);
}
});
扩展:嵌套的异步请求,第一个网络请求获取第二个网络请求所需要的参数
networkClient.token() // 返回 Observable<String>,在订阅时请求 token,并在响应后发送 token
.flatMap(new Function<String, Observable<Messages>>() {
@Override
public Observable<Messages> apply(String token) {
// 返回 Observable<Messages>,在订阅时请求消息列表,并在响应后发送请求到的消息列表
return networkClient.messages(token);
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Messages>() {
@Override
public void accept(Messages messages) {
// 处理显示消息列表
showMessages(messages);
}
});
- doOnNext()
输出元素的准备工作,一般用于获取网络数据后保存到数据库的操作,因为数据库操作是耗时任务需要在子线程执行
Observable.fromIterable(arrayList).flatMap(new Function<List<String>, Observable<String>>() {
@Override
public Observable<String> apply(List<String> strings) throws Exception {
return Observable.fromIterable(strings);
}
}).doOnNext(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "输出元素之前的准备工作: "+s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "accept: " + s);
}
});
- doOnSubscribe()
Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下, doOnSubscribe() 执行在 subscribe() 发生的线程;而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。
Observable.fromIterable(arrayList).flatMap(new Function<List<String>, Observable<String>>() {
@Override
public Observable<String> apply(List<String> strings) throws Exception {
Log.i(TAG, "flatMap操作: ");
return Observable.fromIterable(strings);
}
})
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
Log.i(TAG, "事件发送之前:比如请求网络之前ProgressBar转圈操作");
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
.observeOn(Schedulers.io())
.doOnNext(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "输出元素之前的准备工作: "+s);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "accept: " + s);
}
});
//事件打印结果
com.liujian.rxjava I/MainActivity: 事件发送之前:比如请求网络之前ProgressBar转圈操作
com.liujian.rxjava I/MainActivity: flatMap操作:
com.liujian.rxjava I/MainActivity: 输出元素之前的准备工作: A
com.liujian.rxjava I/MainActivity: accept: A
com.liujian.rxjava I/MainActivity: flatMap操作:
com.liujian.rxjava I/MainActivity: accept: B
com.liujian.rxjava I/MainActivity: 输出元素之前的准备工作: C
com.liujian.rxjava I/MainActivity: accept: C
- 线程控制-Scheduler(线程调度器)
在默认情况下,即在不指定线程的情况下,RxJava遵循的是在哪个线程生产事件,就在哪个线程消费事件
- 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。
- 另外,RxAndroid 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。
Observable.just(1,2,3,4)
.subscribeOn(Schedulers.io()) //改变调用它之前代码的线程
.observeOn(AndroidSchedulers.mainThread()) //改变调用它之后代码的线程
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
//主线程显示数据
}
});
- 线程控制-Scheduler(二)
实现线程的多次切换
注意:不同于 observeOn() , subscribeOn() 的位置放在哪里都可以,但它是只能调用一次的。
Observable.just(R.mipmap.ic_launcher)
.subscribeOn(Schedulers.io())//指定Observable的操作运行在io()中
.observeOn(Schedulers.newThread())//指定map运行于newThread()中
.map(new Function<Integer, Drawable>() {
@Override
public Drawable apply(Integer integer)throws Exception {
return getResources().getDrawable(integer);
}
})
.observeOn(AndroidSchedulers.mainThread())//指定Subscriber的代码运行在主线程
.subscribe(new Consumer<Drawable>() {
@Override
public void accept(Drawable drawable) throws Exception {
iv_iamgeview.setImageDrawable(drawable);
}
});
- 使用场景和使用方式
- 与Retrofit结合使用
getUser(userId)
//接口返回数据后把数据存到数据库,当onNext发生时,它被调用,不改变数据流。
.doOnNext(new Consumer<User>() {
@Override
public void accept(User user) {
saveOrUpdate2Db(user);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});
- Disposable简介
在RxJava中,用它来切断Observer(观察者)与Observable(被观察者)之间的连接,当调用它的dispose()方法时,
它就会将Observer(观察者)与Observable(被观察者)之间的连接切断, 从而导致Observer(观察者)收不到事件。
注意:当切断被观察者与观察者之间的联系,Observable(被观察者)的事件却仍在继续执行。
- Flowable/Subscriber
Observable和Observer的观察者模式是不支持背压的。所以,当我们使用Observable/Observer的时候,我们需要考虑的是,数据量是不是很大(官方给出以1000个事件为分界线,仅供各位参考)
背压:事件产生的速度远远快于事件消费的速度,最终导致数据积累越来越多,从而导致OOM等异常。
需要强调两点:
- 背压策略的一个前提是异步环境,也就是说,被观察者和观察者处在不同的线程环境中。
- 背压(Backpressure)并不是一个像flatMap一样可以在程序中直接使用的操作符,他只是一种控制事件流速的策略。
/* 背压(在异步过程中,由于被观察者发射数据过快,而观察者处理数据不及时, * 导致内存里堆积了太多数据,从而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);
}