一、观察者模式
观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
两种观察者模式:
- Observable ( 被观察者 ) / Observer ( 观察者 )
- Flowable (被观察者)/ Subscriber (观察者)
背压(backpressure):
对于可丢弃的事件,上游生产过快导致事件堆积,当堆积到超出buffer缓冲区上限,就叫做Backpressure出现
在rxjava中会经常遇到一种情况就是被观察者发送消息太快以至于它的操作符或者订阅者不能及时处理相关的消息。那么随之而来的就是如何处理这些未处理的消息。
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
for (int i = 0; ; i++) {//无限循环发事件
emitter.onNext(i);
}
}
}).subscribeOn(Schedulers.io()) //上游在io线程
.observeOn(AndroidSchedulers.mainThread()) //下游在主线程
.subscribe(new Consumer() {
@Override
public void accept(Integer integer) throws Exception {
Thread.sleep(2000);
Log.d(TAG, "" + integer);
}
});
有两种避免这种情况的方法:
- 1:从数量上进行治理, 减少发送的事件
比如按一定条件过滤出需要的事件
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
for (int i = 0; ; i++) {
emitter.onNext(i);
}
}
}).subscribeOn(Schedulers.io())
.filter(new Predicate() {
@Override
public boolean test(Integer integer) throws Exception {
return integer % 10 == 0;//只取能整除的部分
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "" + integer);
}
});
//或者暴力一点,用onBackpressureDrop,如果消费者无法处理数据,则它就把该数据丢弃了。
Observable.interval(1, TimeUnit.MILLISECONDS)
.onBackpressureDrop()
.observeOn(Schedulers.newThread())
.subscribe(
i -> {
System.out.println(i);
try {
Thread.sleep(100);
} catch (Exception e) { }
},
System.out::println);
- 2:从速度上进行治理, 减缓事件发送的速度
比如:操作符中 sample(
) 、 throttleLast( )
、 throttleFirst( )
、throttleWithTimeout( )
、debounce( )
可以通过调节速率来改变Observable发射消息的速度。
建立“响应式拉动(reactive pull)”backpressure
也就是观察者调用request(n)
来向被观察者告知自己的事件处理能力,从而控制被观察者发送事件的数量
二、基本使用
①.被观察者(Observable)的创建
//被观察者,假如这是数不到3的G胖
Observable halflife = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext("半条命1出啦");
emitter.onNext("半条命2出啦");
emitter.onNext("没啦");
emitter.onComplete();
emitter.onNext("半条命3出啦!");//嗯哼
}
});
//也可以是
Observable halflife = Observable.just("半条命1出啦","半条命2出啦","没啦");
//或者
String[] words = {"半条命1出啦","半条命2出啦","没啦"};
Observable halflife = Observable.from(words); //from还可以从callable,publisher,future中获取
//两种都会依次调用
//emitter.onNext("半条命1出啦");
//emitter.onNext("半条命2出啦");
//emitter.onNext("没啦");
//emitter.onComplete();
通过create()
方法生成对象,ObservableOnSubscribe
可以当作计划表,泛型T是要操作的对象。重写subscribe方法,里面写具体计划。ObservableEmitter
对象中的Emitter是发射器的意思,有3种方法:void onNext(T value)
、void onError(Throwable error)
、void onComplete()
。onNext
方法可以无限调用,Observer(观察者)所有的都能接收到,onError
和onComplete
是互斥的,Observer(观察者)只能接收到一个,OnComplete
可以重复调用,但是Observer(观察者)只会接收一次,而onError不可以重复调用,第二次调用就会报异常。
注意:如果from()里面执行了耗时操作,就算使用了subscribeOn(Schedulers.io()),也会是在主线程执行,所以耗时操作最好还是用Observable.create(…);
除了create()
,just()
,from()
方法创建以外还有
-
interval()
—>定时发送 -
range(final int start, final int count)
—>定时发送从start
开始count
次 -
repeat(long times)
--- >指定要发多少次,要是times是空的话会无限次发送,相当于repeat(Long.MAX_VALUE)
-
defer
--> 直到有观察者订阅才创建ovservable
并且是给每个观察者创建新的ovservable
-
empty & never & error
---> empty不发出数据但是正常终止,never不发数据也不终止(那有啥用啊。。。),error的话不发数据但是会以错误终止(onerror)
变换操作
Rxjava里面,将发送的事件或事件序列,加工后转换成不同的事件或事件序列,也就是变换操作很常见。
map:对原始Observable发射的每一项数据应用一个你选择的函数,然后返回一个发射这些结果的Observable。默认不在任何特定的调度器上执行。如果指定observeOn
则在observeon的那个线程上进行,没有指定observeon
但是指定了scheduleon
的话就在scheduleon
的线程上。都不指定就在observable
的创建线程上执行
- map 转换是一对一的,原来发射了几个数据,转换之后还是几个
-
map 转换可以改变发射的数据类型
Observable.create(
(ObservableOnSubscribe) e -> {
e.onNext(1);
e.onNext(2);
e.onComplete();
}
).map(new Function(){
@Override
public String apply(Integer integer) throws Exception {
String day;
switch (integer){
case 1: day = "半条命1";
break;
case 2: day = "半条命2";
break;
default: day = "G胖呢?";
break;
}
return day;
}
}).subscribe(new Observer() {
Disposable mdisposable;
@Override
public void onSubscribe(Disposable d) {
mdisposable=d;
}
@Override
public void onNext(String s) {
Log.e(TAG,s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
Log.e(TAG,"Complete");
mdisposable.dispose();
}
});
flatMap:
flatMap()操作符的作用是将被观察者发送的事件序列进行拆分 & 单独转换 在合并成为一个新的事件序列最后在进行发送
原理:将被观察者发送的事件序列进行拆分成一个个事件 在将每个事件都生成创建一个新的Observable对象
每个原始事件都会生成一个新的Observable对象
每个拆分的新的事件生成的新的Observable对象最终会汇总到一个新建总的Observable对象中
新建的总的Observable对象最终将新合并的事件序列发送给观察者Observer
应用场景:无序的将整个被观察者发送的事件序列进行变换(可能是我写太少结果是有序的。。。)
咱去翻源码看看吧。。。
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final Observable flatMap(Function super T, ? extends ObservableSource extends R>> mapper,
boolean delayErrors, int maxConcurrency, int bufferSize) {
ObjectHelper.requireNonNull(mapper, "mapper is null");
ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency");
ObjectHelper.verifyPositive(bufferSize, "bufferSize");
if (this instanceof ScalarCallable) {
@SuppressWarnings("unchecked")
T v = ((ScalarCallable)this).call();
if (v == null) {
return empty();
}
return ObservableScalarXMap.scalarXMap(v, mapper);
}
return RxJavaPlugins.onAssembly(new ObservableFlatMap(this, mapper, delayErrors, maxConcurrency, bufferSize));//可以看到这里生成了一个ObservableFlatMap
}
顺着ObservableFlatMap
走,看它subscribeActual
的实现
@Override
public void subscribeActual(Observer super U> t) {
if (ObservableScalarXMap.tryScalarXMapSubscribe(source, t, mapper)) {
return;
}
source.subscribe(new MergeObserver(t, mapper, delayErrors, maxConcurrency, bufferSize));
}
也就是外部装饰成一个MergeObserver
,再看它的onNext
实现
@Override
public void onNext(T t) {
// safeguard against misbehaving sources
if (done) {
return;
}
ObservableSource extends U> p;
try {
p = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource");
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
s.dispose();
onError(e);
return;
}
if (maxConcurrency != Integer.MAX_VALUE) {
synchronized (this) {
if (wip == maxConcurrency) {
sources.offer(p);
return;
}
wip++;
}
}
subscribeInner(p);
}
默认调用的flatmap 的 maxConcurrency
大小是 Integer.MAX_VALUE
,所以最终会调用subscribeInner(p)
,其中这个p是我们传入的Function生成的Observable
。继续往下走:
void subscribeInner(ObservableSource extends U> p) {
for (;;) {
if (p instanceof Callable) {
tryEmitScalar(((Callable extends U>)p));
if (maxConcurrency != Integer.MAX_VALUE) {
synchronized (this) {
p = sources.poll();
if (p == null) {
wip--;
break;
}
}
} else {
break;
}
} else {
InnerObserver inner = new InnerObserver(this, uniqueId++);
if (addInner(inner)) {
p.subscribe(inner);
}
break;
}
}
}
嗯,发现了传给p的是InnerObserver,接着看它的onNext
@Override
public void onNext(U t) {
if (fusionMode == QueueDisposable.NONE) {
parent.tryEmit(t, this);
} else {
parent.drain();
}
}
默认mode应该是NONE,所以会执行MergeObserable
的 tryEmit
方法,该方法中调用了drainLoop()
在这里面循环获取数据并发射。
过滤操作:
可以用filter
,elementAt & firstElement & lastElement
,distinct
等各种方法来对发送的事件进行过滤
Observable.range(1,10).filter(i -> i > 5).subscribe(System.out::println);
Observable.range(1, 10).elementAt(0).subscribe(System.out::println);
②.观察者的创建
Observer user = new Observer() {
@Override
public void onSubscribe(Disposable d) {
mDisposable =d;
Log.e(TAG,"onSubscribe");
}
@Override
public void onNext(String s) {
Log.e(TAG,"onNext:"+s);
}
@Override
public void onError(Throwable e) {
Log.e(TAG,"onError"+e.getMessage());
mDisposable.dispose();
}
@Override
public void onComplete() {
Log.e(TAG,"complete");
mDisposable.dispose();
}
};
onNext()
、onError()
、onComplete()
都是跟被观察者发射的方法一一对应的,这里就相当于接收了。Disposable英文意思是可随意使用的,这里就相当于读者和连载小说的订阅关系,如果读者不想再订阅该小说了,可以调用 mDisposable.dispose()取消订阅。(如果在onNext里面dispose,那么onError,onComplete都会收不到)
除了 Observer
接口之外,RxJava 还内置了一个实现了 Observer
的抽象类:Subscriber
。 Subscriber
对 Observer
接口进行了一些扩展,但他们的基本使用方式完全一样。(不过Subscriber
相比较observer来说主要是多了onStart()
和unsubscribe()
两个方法,用后者)
③.建立订阅关系
halflife.subscribe(user);//一行代码搞定(上面也有各种版本的)
输出结果:
这样就是rxjava最简单的用法。
RxJava的异步和链式编程
RxJava是支持异步的,但是RxJava是如何做到的呢?这里就需要Scheduler(调度器)
。它是RxJava用来控制线程。当我们没有设置的时候,RxJava遵循哪个线程产生就在哪个线程消费的原则,也就是说线程不会产生变化,始终在同一个。一般使用RxJava都是后台执行,前台调用,本着这个原则,我们需要调用observeOn(AndroidSchedulers.mainThread())
,observeOn
是事件回调的线程,AndroidSchedulers.mainThread()一看就知道是主线程,subscribeOn(Schedulers.io()),subscribeOn是事件执行的线程,Schedulers.io()是子线程,这里也可以用Schedulers.newThread(),只不过io线程可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。
//改一下上面的代码
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext("半条命1出啦");
emitter.onNext("半条命2出啦");
emitter.onNext("没啦");
emitter.onComplete();
}
}).observeOn(AndroidSchedulers.mainThread())//回调在主线程
.subscribeOn(Schedulers.io())//执行的子线程
.subscribe(new Observer() {@Override
public void onSubscribe(Disposable d) {
mDisposable =d;
Log.e(TAG,"onSubscribe");
}
@Override
public void onNext(String s) {
Log.e(TAG,"onNext:"+s);
}
@Override
public void onError(Throwable e) {
Log.e(TAG,"onError"+e.getMessage());
mDisposable.dispose();
}
@Override
public void onComplete() {
Log.e(TAG,"complete");
mDisposable.dispose();
}
});
如果只用关心onNext方法里的内容,可以直接重载subscribe(Consumer spuer T> onNext)这个方法,会减少代码,当然一开始还是建议创建Observer对象。
线程调度—— Scheduler 与 Worker
Scheduler 与 Worker 在 RxJava2 中是一个非常重要的概念,他们是 RxJava 线程调度的核心与基石。
Rxjava2 Schedulers
中默认有多种shceduler
的实现:
方法 | 说明 |
---|---|
single() | 每个worker都在同一个线程上执行 |
computation() | 适用于计算密集型任务 |
io() | 适用于 IO 密集型任务 |
trampoline() | 在调用schedule的线程执行 |
newThread() | 和single相反,它每个worker都对应一个新的线程 |
一个 Scheduler 可以创建多个 Worker,这两者
是一对多的关系,而 Worker 与 Task 也是一对多
的关系。
对于worker:同一个worker创建的task都是串行的,执行的任务符合队列(先进先出),worker会与调用它的方法的runnable
绑定,如果worker取消了,它的task也会都取消
因此当有操作符需要使用 Scheduler 时,可以通过 Worker 来将一系列的 Runnable 统一的调度和取消,最典型的例子就是observeOn
,下面会尝试分析。
/**
* Retrieves or creates a new {@link Scheduler.Worker} that represents serial execution of actions.
*
* When work is completed it should be unsubscribed using {@link Scheduler.Worker#dispose()}.
*
* Work on a {@link Scheduler.Worker} is guaranteed to be sequential.
*
* @return a Worker representing a serial queue of actions to be executed
*/
@NonNull
public abstract Worker createWorker();
@NonNull
public Disposable scheduleDirect(@NonNull Runnable run) {
return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
}
public Disposable schedulePeriodicallyDirect(@NonNull Runnable run, long initialDelay, long period, @NonNull TimeUnit unit)
Rxjava2中有scheduleDirect / schedulePeriodicallyDirect这两个方法简化对worker的调用,相当于创建了一个只调度一次任务的worker并立即调度任务。
这里只挑io()说一下吧。大致原理图是这样的IoScheduler:
ioscheduler的线程数是没有上限的,因为 IO 设备的速度是远远低于 CPU 速度。等待io操作时cpu一般时闲置的。所以可以创建更多的线程来尽量充分利用cpu
CachedWorkerPool:
CachedWorkerPool
是一个变长并定期清理的ThreadWorker
的缓存池,内部通过一个ConcurrentLinkedQueue
维护。和PoolWorker
类似,ThreadWorker
也是继承自NewThreadWorker
:
static final class ThreadWorker extends NewThreadWorker {
private long expirationTime;
ThreadWorker(ThreadFactory threadFactory) {
super(threadFactory);
this.expirationTime = 0L;
}
public long getExpirationTime() {
return expirationTime;
}
public void setExpirationTime(long expirationTime) {
this.expirationTime = expirationTime;
}
}
expirationTime
表示超时时间,在CacheWorkerPool
初始化的时候会传入worker超时时间,这个时间表示ThreadWorker
闲置后的最长存在时间(当然,实际中也不一定有那么长)
EventLoopWorker
在ComputationScheduler
中有EventLoopWorker
这个类,它的作用主要是
- 管理自身调度过的任务
- ThreadWorker的管理,回收和再次使用
Worker的管理
- 创建: 会现在队列里找闲置的
ThreadWorker
,有的话就取出,没有就会新建一个,在外面包装一层EventLoopWorke
并返回
+回收:dispose后,会把threadworker
放进闲置队列并更新超时时间
+清除:CacheWorkerPool的定时任务,每隔一个超时时间(60s)清除队列里的超时的threadworker
使用场景举例
与retrofit联用
先是retrofit的这几个:
private var okhttp = OkHttpClient.Builder()
.addInterceptor(
HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY))
.connectTimeout(8, TimeUnit.SECONDS)
.build()
private val client = Retrofit.Builder()
.baseUrl("https://news-at.zhihu.com/")
.client(okhttp)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
private val api:Service = client.create(Service::class.java)
//别忘了这个(哭)
RetrofitUtil retro = new RetrofitUtil();
retro.getNews()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Data data) {
mdata = data;
}
});
偷了点懒,不过简单实现基本上是这样。
如有哪些地方说得不对还望指正
参考文章:
RxJava2 系列 -1:一篇的比较全面的 RxJava2 方法总结(WngShhng):https://www.jianshu.com/p/823252f110b0
RxJava 2.0中backpressure(背压)概念的理解(一叶飘舟):
https://blog.csdn.net/jdsjlzx/article/details/52717636
RxJava2源码解读之 Map、FlatMap(三好码农)
https://www.jianshu.com/p/122abe149ac4
深入理解 RxJava2:Scheduler(2)(蝶翼的罪)
https://www.jianshu.com/p/b742526c7dec
使用Retrofit+RxJava实现网络请求(JYcoder)
https://www.jianshu.com/p/092452f287db