RxJava简介
RxJava是现在大火的一个异步框架,他代码简洁好懂,功能强大,扩展性高,维护难度小。RxJava最吸引人的功能就是它的线程切换功能。在Android开发中,UI组件只能在主线程中进行,但是主线程中执行太复杂的逻辑又会导致APP卡顿,因此灵活的线程切换是一个安卓开发工程师的必会技能。RxJava提供了优秀的线程切换能力,能在不同的线程执行规定的逻辑代码。
这次就来聊一聊RxJava是怎么实现线程切换。
首先RxJava的一切转换功能都是基于lift这个方法产生代理对象完成,关于这部分我之前有写过一篇博客。
RxJava的源码分析(map,flatmap和类型变换)
大家可以先了解一下lift方法的工作原理,本文就不在赘述了。
示例代码
public class RxJavaTest_1 {
public static void main(String[] args) throws InterruptedException {
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
//打印call方法所在线程
System.out.println("call : " + Thread.currentThread());
subscriber.onNext("doOnNext");
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
System.out.println("onCompleted : " + Thread.currentThread());
}
@Override
public void onError(Throwable throwable) {
System.out.println("onError : " + Thread.currentThread());
}
@Override
public void onNext(String s) {
//答应onNext方法所在线程
System.out.println("onNext : " + Thread.currentThread());
}
});
Thread.sleep(1000);
//打印主线程
System.out.println("main : " + Thread.currentThread());
}
}
RxJava提供subscribeOn和observeOn两个方法去更换线程,subscribeOn可以指定subscribe方法运行的线程,observeOn可以指定Subscriber对象的方法被回调时执行的线程。
下图是上面代码的执行结果
我指定subscribe方法运行和Subscriber对象的方法被回调时执行都是在新线程中进行。因此可以看到call方法是运行在一个新线程中的,而onNext和onCompleted两个则是运行在另外的一个线程中的,并且这两个线程都不是主线程。
线程转换源码分析
subscribeOn()和observeOn()两个转换线程的方式都差不多,因此我就用subscribeOn()进行分析。
首先进入subscribeOn()方法。
(注:本文中截取的代码并不是完整的源码,只截取核心代码)
public final Observable<T> subscribeOn(Scheduler scheduler) {
return create(new OperatorSubscribeOn(this, scheduler));
}
Scheduler对象是用来描述线程的抽象类,这里需要一个Scheduler对象,由于我们使用的newThread()方法得到了Scheduler,因此目前的Scheduler引用指向的是NewThreadScheduler实现类。
接着产生了一个OperatorSubscribeOn对象。看类名就知道这是个转换器类,而且是用来转换线程的。
来看看这个类吧。
public final class OperatorSubscribeOn<T> implements OnSubscribe<T> {
final Scheduler scheduler;
final Observable<T> source;
public OperatorSubscribeOn(Observable<T> source, Scheduler scheduler) {
this.scheduler = scheduler;
this.source = source;
}
.....
}
这个类中记录了原始的Observable和目标线程的信息,而且还实现OnSubscribe接口,我好像隐隐感觉到了什么。
接着看这个create方法,这个方法大家都很熟悉的啊,就是用来创建Observable对象的。
public static <T> Observable<T> create(Observable.OnSubscribe<T> f) {
return new Observable(hook.onCreate(f));
}
果然是这样没错的。到目前为止,subscribeOn方法执行结束。RxJava又创建了一个代理Observable对象出来。
接着我们调用了代理Observable的subscribe方法,传入了一个Subscriber对象。在subscribe方法中Observable对象使用内部类OnSubscribe调用模板方法call。由于这里OnSubscribe对象是OperatorSubscribeOn这个类,因此来看看它的call方法里有什么。
public void call(final Subscriber<? super T> subscriber) {
//创建一个Worker对象
final Worker inner = this.scheduler.createWorker();
subscriber.add(inner);
inner.schedule(new Action0() {
public void call() {
final Thread t = Thread.currentThread();
//产生代理Subscriber
Subscriber<T> s = new Subscriber<T>(subscriber) {
public void onNext(T tx) {
subscriber.onNext(tx);
}
public void onError(Throwable e) {
try {
subscriber.onError(e);
} finally {
inner.unsubscribe();
}
}
public void onCompleted() {
try {
subscriber.onCompleted();
} finally {
inner.unsubscribe();
}
}
public void setProducer(final Producer p) {
subscriber.setProducer(new Producer() {
public void request(final long n) {
if (t == Thread.currentThread()) {
p.request(n);
} else {
inner.schedule(new Action0() {
public void call() {
p.request(n);
}
});
}
}
});
}
};
//操作代理对象
OperatorSubscribeOn.this.source.unsafeSubscribe(s);
}
});
}
这个call方法就是线程转换的核心方法。
首先在call方法中创建了一个Worker对象。
我们使用的是NewThreadScheduler示例,因此产生的是一个NewThreadWorker对象。这里要看看NewThreadWorker的构造方法。
public NewThreadWorker(ThreadFactory threadFactory) {
ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, threadFactory);
boolean cancelSupported = tryEnableCancelPolicy(exec);
if (!cancelSupported && exec instanceof ScheduledThreadPoolExecutor) {
registerExecutor((ScheduledThreadPoolExecutor)exec);
}
this.schedulersHook = RxJavaPlugins.getInstance().getSchedulersHook();
this.executor = exec;
}
看到它悄悄创建了一个线程池,还是只有一个核心线程的那种。
然后这个对象又调用了schedule()方法,这个方法需要一个Action0实参,那就给他创建一个,这个Action0对象实现了call()方法,call方法中新建了一个Subscriber类型代理,回调了原始call()方法传入的Subscriber对象的方法,然后在底部的OperatorSubscribeOn.this.source.unsafeSubscribe(s)这行代码使用了代理对象。
现在有了Action0对象,进入schedule方法。
public Subscription schedule(Action0 action) {
return this.schedule(action, 0L, (TimeUnit)null);
}
public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) {
return this.scheduleActual(action, delayTime, unit));
}
public ScheduledAction scheduleActual(Action0 action, long delayTime, TimeUnit unit) {
Action0 decoratedAction = this.schedulersHook.onSchedule(action);
ScheduledAction run = new ScheduledAction(decoratedAction);
Object f;
if (delayTime <= 0L) {
f = this.executor.submit(run);
} else {
f = this.executor.schedule(run, delayTime, unit);
}
run.add((Future)f);
return run;
}
一路来到scheduleActual()方法。Action0对象被包装了一下,这里默认的情况下就是返回原本的Action0对象。
下面利用Action0对象生成了一个ScheduledAction对象,继续往下看。
executor.submit(run)。
果然使用了线程池执行了ScheduledAction对象的代码。
哎,等等,submit方法不是只接收Callable实现类和Runable实现类么?
现在来看看ScheduledAction类。
public final class ScheduledAction extends AtomicReference<Thread> implements Runnable, Subscription {
final Action0 action;
public ScheduledAction(Action0 action) {
this.action = action;
}
public void run() {
try {
this.action.call();
} catch (Throwable var7) {
......
} finally {
this.unsubscribe();
}
}
}
ScheduledAction类果然实现了Runnable接口,并且存储了Action0的信息。它的run方法中调用了Action0的call()方法。
回顾一下Action0的call方法中有什么,一个代理的Subscriber对象,然后还用 OperatorSubscribeOn.this.source.unsafeSubscribe(s)操作了这个代理对象。这里的source就是之前保存的原始Observable对象。这不就是代理Observable对原始Observable对象的回调嘛。到这里整个流程应该就有点眉目了。
再进入unsafeSubscribe()方法。
public final Subscription unsafeSubscribe(Subscriber<? super T> subscriber) {
try {
subscriber.onStart();
hook.onSubscribeStart(this, this.onSubscribe).call(subscriber);
return hook.onSubscribeReturn(subscriber);
} catch (Throwable var6) {
Throwable e = var6;
Exceptions.throwIfFatal(var6);
try {
subscriber.onError(hook.onSubscribeError(e));
} catch (Throwable var5) {
Exceptions.throwIfFatal(var5);
RuntimeException r = new RuntimeException("Error occurred attempting to subscribe [" + var6.getMessage() + "] and then again while trying to pass to onError.", var5);
hook.onSubscribeError(r);
throw r;
}
return Subscriptions.unsubscribed();
}
}
这个方法就是整个拼图的最后一块。在这个方法中,原始Observable对Subscriber对象进行了全套的操作,onStart()方法,onError()方法这些个流程方法都有,还有原始Observable中内部对象OnSubscribe的call方法,这里包含了执行onNext()方法的逻辑。
示例代码执行(只包含subscribeOn方法)
首先创建了一个Observable对象OA,实现了OnSubscribe对象并重写了call方法。
执行subscribeOn()方法。在这个方法中通过OperatorSubscribeOn对象记录了原始的OA,并创建了代理对象OB。
执行subscriber()方法。传入一个Subscriber对象SA。这时代理对象OB会执行call方法,call方法中有创建了包含线程池的Worker对象,调用Work对象scheduler方法,该方法正是把一个任务加入到线程池,由一个新线程去执行任务。
这个任务则是由Action0实例构成ScheduledAction对象,其run方法中调用了Action0的call方法。
Action0的call方法中是对SA进行一个代理,产生代理对象SB,最后由上层的OA操作SB。SB又回调了嵌套其中的SA的方法,然后运行结束,方法出栈。
小结
可以看出,RxJava的线程变换也是基于对象实现的。新线程得执行时基于一个新的代理Observable完成,并回调原始的Observable的方法。这样有个好处就是对整个处理流程的影响非常小。可以通过map或flatmap产生多个Observable,进而多次转换线程,给RxJava带来无限可能。
这篇文章只分析了Schedulers.newThread()的情况, 其他情形也是类似的,大家可以自己查看源码。
源码分析的文章都比较长,谢谢你能看到这里,如果文章中有错误的地方或者更好的建议,希望你能指出噢。