被压模式不了解的可以自行百度,有详细的资料,这里不再详述。RxJava中被压模式体现在使用中即Flowable+Subscrible模式。
简单的使用分为三步:
1,创建Flowable
Flowable flowable = Flowable.create(new FlowableOnSubscribe() {
@Override
public void subscribe(FlowableEmitter e) throws Exception {
e.onNext("HelloWord");
}
//抛弃策略
}, BackpressureStrategy.DROP);
上面是非常简单的Flowable创建过程,看创建的源码,需要传入一个FlowableOnSubscribe用于想消息队列(其实使用AtomicReferenceArray实现多线程同步)中添加消息,和一个BackpressureStrategy指定消息抛弃策略,此处我们详细讲述默认策略,其他的后续补充。
看下创建Flowable的源码:
public static Flowable create(FlowableOnSubscribe source, BackpressureStrategy mode) {
ObjectHelper.requireNonNull(source, "source is null");
ObjectHelper.requireNonNull(mode, "mode is null");
return RxJavaPlugins.onAssembly(new FlowableCreate(source, moe)
);}
其实就是创建了一个FlowableCreate并返回并回了
2,创建Subscrible
Subscriber subscriber = new Subscriber() {
Subscription subscription;
@Override
public void onSubscribe(Subscription s) {
subscription.request(1);
}
@Override
public void onNext(String s) {
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: " + e.getLocalizedMessage());
}
@Override
public void onComplete() {
subscription.cancel();
}
};
3,把两者联系起来
flowable.subscribe(subscriber);
来看源码:
s = RxJavaPlugins.onSubscribe(this, s);
ObjectHelper.requireNonNull(s, "Plugin returned null Subscriber");
subscribeActual(s);
主要是者三行代码实现绑定,
看第一句源码:
public static Subscriber super T> onSubscribe(Flowable source, Subscriber super T> subscriber) {
BiFunction f = onFlowableSubscribe;
if (f != null) {
return apply(f, source, subscriber);
}
return subscriber;
}
是设计用来在真正绑定之前让我们对所有的订阅者做拦截,可以对其做相应的处理,当然如果不需要我们也可以不设置
就直接返回。
第二句用于判空抛异常,这种写法会优美一些,也方便对异常的统一处理。
第三句调用subscribeActual(s)方法,并把Subscriber传下去,subscribeActual(s)是一个抽象方法,我们直接看其在FlowableCreate
中的实现:
@Override
public void subscribeActual(Subscriber super T> t) {
BaseEmitter emitter;
switch (backpressure) {
case MISSING: {
emitter = new MissingEmitter(t);
break;
}
case ERROR: {
emitter = new ErrorAsyncEmitter(t);
break;
}
case DROP: {
emitter = new DropAsyncEmitter(t);
break;
}
case LATEST: {
emitter = new LatestAsyncEmitter(t);
break;
}
default: {
emitter = new BufferAsyncEmitter(t, bufferSize());
break;
}
}
t.onSubscribe(emitter);
try {
source.subscribe(emitter);
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
emitter.onError(ex);
}
}
看到会根据不同的抛弃策略选择不同的消息发射器,这里我们看下默认发射器BufferAsyncEmitter。
@Override
public void onNext(T t) {
if (done || isCancelled()) {
return;
}
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
queue.offer(t); //code1
drain(); //code2
}
可以看到onNext做了两件事,首先code1向消息队列中添加消息,code2的作用是从消息队列中拿消息给Subscriber执行,下面我们重点分析下这个方法(这段逻辑
的设计也是让我眼前一亮):
void drain() {
if (wip.getAndIncrement() != 0) {
return;
}
int missed = 1;
...
...
for(;;){
missed = wip.addAndGet(-missed);
if (missed == 0) {
break;
}
}
}
先来看这个地方,wip是AtomicInteger类型,保证自增的原子性操作,这个地方保证了,同一时间只有一个线程在操作循环消息队列取出消息,作用类似于添加synchronized,
当然只是类似。可以看到如果wip不等0,说明还有线程在操作消息队列,就把wip加1返回,表示我要取消息,但是检测都有线程正在取,就把标记wip自增1,然后在最后等本次
操作结束后调用wip.addAndGet(-missed)实际就是(wip-1)如果等于0就结束退出,如果大于0,说明有错过的请求,再次循环,直到wip等0.那么为什么会有这样的设计呢,为什
么不是,一次把所有请求都执行呢,其实这个跟Flowable的消息存取模式有关。我们先来看下Subscription的request接口的介绍:
/**
* No events will be sent by a {@link Publisher} until demand is signaled via this method.
*
* It can be called however often and whenever needed—but the outstanding cumulative demand must never exceed Long.MAX_VALUE.
* An outstanding cumulative demand of Long.MAX_VALUE may be treated by the {@link Publisher} as "effectively unbounded".
*
* Whatever has been requested can be sent by the {@link Publisher} so only signal demand for what can be safely handled.
*
* A {@link Publisher} can send less than is requested if the stream ends but
* then must emit either {@link Subscriber#onError(Throwable)} or {@link Subscriber#onComplete()}.
*
* @param n the strictly positive number of elements to requests to the upstream {@link Publisher}
*/
public void request(long n);
讲的很明白,消费端(Subscriber)只有调用了request方法,服务端(Flowable)才会发射数据,也就是说在Flowable中调用FlowableEmitter的onNext方法,只是把消息放到消息
队列,如果Subscriber中不调用Subscription的request方法,是不会发送消息的。那么我们看下request的实现:
@Override
public final void request(long n) {
if (SubscriptionHelper.validate(n)) {
BackpressureHelper.add(this, n);//code1
onRequested(); //code2
}
}
request方法的实现在父类BaseEmitter中,本身继承了AtomicLong,code1的作用是把自己的值加上要请求的数量的结果赋值给自己(前面讲到自己本身也是AtomicLong),然后调用 onRequested()
方法,看下 onRequested()的具体实现:
@Override
void onRequested() {
drain();
}
看到有调用了drain();方法,那么我们接着看drain()是如何取消息的:
void drain() {
if (wip.getAndIncrement() != 0) {
return;
}
int missed = 1;
final Subscriber super T> a = actual;
final SpscLinkedArrayQueue q = queue;
for (;;) {
long r = get();
long e = 0L;
while (e != r) {
...
boolean d = done;
T o = q.poll();
boolean empty = o == null;
...
if (empty) {
break;
}
...
a.onNext(o);
e++;
}
...
if (e != 0) {
BackpressureHelper.produced(this, e);
}
missed = wip.addAndGet(-missed);
if (missed == 0) {
break;
}
}
}
仅摘取了关键代码,可以看到早while循环中,只有当e != r才会进入取消息执行,那么r的值是什么呢,其实他就是我们前面说到的request请求的值,我们说过他本身就是一个AtomicLong
理解了这里就知道为什么不调用request就不会发消息了吧。在往下看:
if (e != 0) {
BackpressureHelper.produced(this, e);
}
这段逻辑是把已经处理的消息减去,其实就是request请求的数据条数减去已经处理的数据条数。
if (empty) {
break;
}
可以看到如果发起request(int n)请求的时候消息队列为空就返回,但是这时要请求的数据条数已经是n了,即long r = get() r等于n,所以当有消息添加的时候会立刻执行。这个设计是不是
很巧妙地实现了消息队列阻塞的功能又没有任何开销。
好了到这里被压模式基本介绍完了,有不少地方没有详尽介绍,需要自行阅读源码,不好的地方,不喜勿喷。