前言
前面用了两篇文章介绍了一下Rx的封装套路以及涉及线程调度流程控制的subscribeOn与observeOn源码。本文会对Android开发中常用的IoScheduler和AndroidSchedulers.mainThread()进行源码解析。前文也提到过,subscribeOn与observeOn只是基于开发者设置的scheduler在流程上进行线程调度的控制。这里可以理解为相对于subscribeOn与observeOn,scheduler只是它们的工具。 所以本文将重点看看scheduler的实现(ps:本文基于RxJava 2.1.16,RxAndroid 2.0.1)。
IoScheduler
基于subscribeOn在subscribeActual时的调用,整理了一个流程图: 上面给出的是从subscribeActual调用scheduler开始到提交给线程池的数据流。接下来我们将按这个过程来解析IoScheduler。
Observable.create<String> {
Log.d(TAG, "事件产生线程:${Thread.currentThread().name}")
it.onNext("I'm ")
it.onComplete()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.d(TAG, "事件消费线程:${Thread.currentThread().name}")
Log.d(TAG, it)
}
Scheduler
Scheduler.java是RxJava中线程调度者scheduler的父类。ObservableSubscribeOn.subscribeActual方法中,最终会调用scheduler.scheduleDirect进行线程切换。这里的scheduler为IoScheduler,scheduleDirect方法在其父类实现。
- 调用子类实现的createWorker方法。
- 利用Scheduler.DisposeTask类(也是一个Runnable)包装传入的Runnable对象。
- 调用Scheduler.Worker对象的schedule方法进行线程调度。 接下来,以此这个方法中为基础,进行逐一解析。
// ObservableSubscribeOn.java
@Override
public void subscribeActual(final Observer<? super T> s) {
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
s.onSubscribe(parent);
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
// Scheduler.java
@NonNull
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
final Worker w = createWorker();
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
DisposeTask task = new DisposeTask(decoratedRun, w);
w.schedule(task, delay, unit);
return task;
}
...
@NonNull
public abstract Worker createWorker();
线程调度
在IoScheduler中,会涉及到两个逻辑的线程池:1、工作线程池,用于处理外部提交过来的任务(譬如开发者设置的订阅或观察的响应。)2、回收线程池,定期回收上述过期的工作线程池的线程池。
这里先说一下IoScheduler的内部类,好让大家对它们有初步认识。
- IoScheduler.ThreadWorker:IoScheduler中真正管理线程的类,成员变量有一个Java线程池,用于线程调度。
- IoScheduler.CachedWorkerPool:这是一个工厂角色的类型,主要用于创建、缓存复用和销毁IoScheduler.ThreadWorker。
- IoScheduler.EventLoopWorker:IoScheduler.ThreadWorker的装饰器,用于控制CachedWorkerPool管理它自身持有的ThreadWorker。
成员变量
先来看看IoScheduler的成员变量及其定义:
- 这里的XXX_THREAD_NAME_PREFIX及RxThreadFactory对象为用于线程池创建线程的配置,RxThreadFactory为Java的ThreadFactory子类,这里主要用于规范线程的名称(name)及优先级(priority)。在IoScheduler的静态代码块用到。
- KEEP_ALIVE_TIME、KEEP_ALIVE_UNIT:工作线程调用结束后默认的存活时间。后续的start()方法会用到。
- SHUTDOWN_THREAD_WORKER:IoScheduler.ThreadWorker对象,用于在Rx的dipose调用后的线程后续处理。
- threadFactory:这是一个在构造方法可自定义设置的ThreadFactory,用于覆盖上述的WORKER_THREAD_FACTORY。
- pool:IoScheduler.CachedWorkerPool对象,这是一个类似工厂模式的对象,主要用于创建上述的IoScheduler.ThreadWorker对象,管理及复用线程。
ps:这里可以简单阅读有一个了解,后面会详细介绍并将它们在流程上串联起来。
// IoScheduler.java
public final class IoScheduler extends Scheduler {
private static final String WORKER_THREAD_NAME_PREFIX = "RxCachedThreadScheduler";
static final RxThreadFactory WORKER_THREAD_FACTORY;
private static final String EVICTOR_THREAD_NAME_PREFIX = "RxCachedWorkerPoolEvictor";
static final RxThreadFactory EVICTOR_THREAD_FACTORY;
private static final long KEEP_ALIVE_TIME = 60;
private static final TimeUnit KEEP_ALIVE_UNIT = TimeUnit.SECONDS;
static final ThreadWorker SHUTDOWN_THREAD_WORKER;
final ThreadFactory threadFactory;
final AtomicReference<CachedWorkerPool> pool;
/** The name of the system property for setting the thread priority for this Scheduler. */
private static final String KEY_IO_PRIORITY = "rx2.io-priority";
static final CachedWorkerPool NONE;
静态代码块
这里主要是创建上述提到的一些静态变量,这里的NONE可以理解为当前IoScheduler的默认CachedWorkerPool对象。用于后续start()和shutdown()方法的更新替换。
KEY_IO_PRIORITY = "rx2.io-priority"是设置线程的优先级,可以通过系统属性设置key为"rx2.io-priority"修改。
static {
SHUTDOWN_THREAD_WORKER = new ThreadWorker(new RxThreadFactory("RxCachedThreadSchedulerShutdown"));
SHUTDOWN_THREAD_WORKER.dispose();
int priority = Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, Integer.getInteger(KEY_IO_PRIORITY, Thread.NORM_PRIORITY)));
WORKER_THREAD_FACTORY = new RxThreadFactory(WORKER_THREAD_NAME_PREFIX, priority);
EVICTOR_THREAD_FACTORY = new RxThreadFactory(EVICTOR_THREAD_NAME_PREFIX, priority);
NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY);
NONE.shutdown();
}
初始化、启动及关闭
Schedulers.io()的调用对应的是默认全局静态IoScheduler对象,用无参构造方法创建。
- threadFactory对象默认为WORKER_THREAD_FACTORY对象。
- 先利用默认CachedWorkerPool对象NONE初始化pool,再调用start()方法。
- start()方法,真正创建一个CachedWorkerPool对象,设置默认线程池存活时间KEEP_ALIVE_TIME(60s),进行替换。ps:这里运用到CAS保证线程安全。
- 后续的shutdown()方法则是start()的重置操作。ps:这里也运用到了自旋机制保证线程安全。
其实笔者个人不是特别理解这里为什么需要进行start、shutdown的逻辑,如果有懂的朋友可以在下方评论交流。
// IoScheduler.java
public IoScheduler() {
this(WORKER_THREAD_FACTORY);
}
public IoScheduler(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
this.pool = new AtomicReference<CachedWorkerPool>(NONE);
start();
}
@Override
public void start() {
CachedWorkerPool update = new CachedWorkerPool(KEEP_ALIVE_TIME, KEEP_ALIVE_UNIT, threadFactory);
if (!pool.compareAndSet(NONE, update)) {
update.shutdown();
}
}
@Override
public void shutdown() {
for (;;) {
CachedWorkerPool curr = pool.get();
if (curr == NONE) {
return;
}
if (pool.compareAndSet(curr, NONE)) {
curr.shutdown();
return;
}
}
}
createWorker
回归正题,Scheduler.scheduleDirect方法会调用createWorker()。在IoScheduler中createWorker()会创建并返回EventLoopWorker对象,而EventLoopWorker的成员变量CachedWorkerPool pool即为IoScheduler的pool对象。
// IoScheduler.java
@NonNull
@Override
public Worker createWorker() {
return new EventLoopWorker(pool.get());
}
EventLoopWorker
EventLoopWorker主要包含了从IoScheduler获取过来的CachedWorkerPool和一个ThreadWorker。
前面提到,CachedWorkerPool在EventLoopWorker中充当对于ThreadWorker的管理。ThreadWorker则利用内部包含的线程池进行线程调度。
在EventLoopWorker的构造方法中,threadWorker通过CachedWorkerPool.get()获取。
ps:CachedWorkerPool.get方法中包含对于ThreadWorker的复用逻辑,这个后面会讲到。
// IoScheduler.java - class EventLoopWorker
static final class EventLoopWorker extends Scheduler.Worker {
private final CompositeDisposable tasks;
private final CachedWorkerPool pool;
private final ThreadWorker threadWorker;
final AtomicBoolean once = new AtomicBoolean();
EventLoopWorker(CachedWorkerPool pool) {
this.pool = pool;
this.tasks = new CompositeDisposable();
this.threadWorker = pool.get();
}
...
Scheduler.scheduleDirect方法的最后会调用w.schedule,这里实际会调用threadWorker.scheduleActual。
// IoScheduler.java - class EventLoopWorker
@NonNull
@Override
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
if (tasks.isDisposed()) {
// don't schedule, we are unsubscribed
return EmptyDisposable.INSTANCE;
}
return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}
ThreadWorker
ThreadWorker的scheduleActual继承自其父类NewThreadWorker:
- scheduleActual方法中真正调用Java线程池executor进行线程调度。
- 这里利用ScheduledRunnable类再次对传入的Runnable对象进行装饰。
- 以ObservableSubscribeOn触发为例,Runnable对象的装饰链为SubscribeTask -> Scheduler.DisposeTask(Scheduler.scheduleDirect方法)-> ScheduledRunnable(NewThreadWorker.scheduleActual)。
// IoScheduler.java - class ThreadWorker
static final class ThreadWorker extends NewThreadWorker {
private long expirationTime;
...
// NewThreadWorker.java
@NonNull
public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
if (parent != null) {
if (!parent.add(sr)) {
return sr;
}
}
Future<?> f;
try {
if (delayTime <= 0) {
f = executor.submit((Callable<Object>)sr);
} else {
f = executor.schedule((Callable<Object>)sr, delayTime, unit);
}
sr.setFuture(f);
} catch (RejectedExecutionException ex) {
if (parent != null) {
parent.remove(sr);
}
RxJavaPlugins.onError(ex);
}
return sr;
}
NewThreadWorker内的默认会创建一个单线程的线程池。这里就要理解一个概念,IoScheduler内不是只有一个线程池进行调度,而是有多个单线程的线程池进行调度。对于每个线程池的管理者为IoScheduler.ThreadWorker。
// NewThreadWorker.java
public class NewThreadWorker extends Scheduler.Worker implements Disposable {
private final ScheduledExecutorService executor;
volatile boolean disposed;
public NewThreadWorker(ThreadFactory threadFactory) {
executor = SchedulerPoolFactory.create(threadFactory);
}
...
// SchedulerPoolFactory.java
public static ScheduledExecutorService create(ThreadFactory factory) {
final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);
tryPutIntoPool(PURGE_ENABLED, exec);
return exec;
}
CachedWorkerPool
填补一下小尾巴,说说CachedWorkerPool.get方法:
- get()方法最终返回一个ThreadWorker对象,可以理解为一个工厂角色。
- expiringWorkerQueue为CachedWorkerPool中对于ThreadWorker的缓存队列(ConcurrentLinkedQueue<ThreadWorker>类型)。
- 若expiringWorkerQueue不为空则从中取出一个ThreadWorker返回,否则创建一个新的ThreadWorker返回。
ps:需要注意的是,ThreadWorker加入expiringWorkerQueue的时机并不在此,而在Runnable执行结束后。这个后面会讲到。
// IoScheduler.java - class CachedWorkerPool
ThreadWorker get() {
if (allWorkers.isDisposed()) {
return SHUTDOWN_THREAD_WORKER;
}
while (!expiringWorkerQueue.isEmpty()) {
ThreadWorker threadWorker = expiringWorkerQueue.poll();
if (threadWorker != null) {
return threadWorker;
}
}
// No cached worker found, so create a new one.
ThreadWorker w = new ThreadWorker(threadFactory);
allWorkers.add(w);
return w;
}
ThreadWorker管理与销毁
在执行完Rx传递过来的Runnable后会存在回收资源问题,接下来说说关于ThreadWorker的管理策略。
在之前的Scheduler.scheduleDirect方法中,还遗漏了Scheduler.DisposeTask(继承自Runnable)。这里初始化时传入的decoratedRun即为上一步传入的SubscribeTask对象,w为上述提到的EventLoopWorker。
// Scheduler.java
@NonNull
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
final Worker w = createWorker();
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
DisposeTask task = new DisposeTask(decoratedRun, w);
...
在run方法被调用时,
- 首先调用的是成员变量Runnable对象的run方法(ps:这里是SubscribeTask,执行下一步订阅逻辑)。
- 最后调用自身的dispose()方法,由于w为EventLoopWorker,故会执行EventLoopWorker.dispose()方法。
// Scheduler.java - class DisposeTask
@Override
public void run() {
runner = Thread.currentThread();
try {
decoratedRun.run();
} finally {
dispose();
runner = null;
}
}
@Override
public void dispose() {
if (runner == Thread.currentThread() && w instanceof NewThreadWorker) {
((NewThreadWorker)w).shutdown();
} else {
w.dispose();
}
}
EventLoopWorker.dispose方法会调用pool.release,这里的pool就是CachedWorkerPool对象,由此也能看出EventLoopWorker有操控CachedWorkerPool对于ThreadWorker管理的作用。
// IoScheduler.java - class EventLoopWorker
@Override
public void dispose() {
if (once.compareAndSet(false, true)) {
tasks.dispose();
// releasing the pool should be the last action
pool.release(threadWorker);
}
}
这里会修改前面我们提到ThreadWorker的expirationTime属性,该过期时间会设置为当前时间+keepAliveTime(默认60s)。然后将threadWorker加入到expiringWorkerQueue队列中。
// IoScheduler.java - class CachedWorkerPool
void release(ThreadWorker threadWorker) {
// Refresh expire time before putting worker back in pool
threadWorker.setExpirationTime(now() + keepAliveTime);
expiringWorkerQueue.offer(threadWorker);
}
CachedWorkerPool其实也是一个Runnable对象,前面提到CachedWorkerPool的另一个作用是定期销毁已过期的IoScheduler.ThreadWorker对象。evictor为执行销毁的线程池,以CachedWorkerPool作为Runnable对象。(ps:这里设置每keepAliveTime时刻触发一次,即默认为60s一次触发)。
// IoScheduler.java - class CachedWorkerPool
static final class CachedWorkerPool implements Runnable
...
CachedWorkerPool(long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) {
...
ScheduledExecutorService evictor = null;
Future<?> task = null;
if (unit != null) {
evictor = Executors.newScheduledThreadPool(1, EVICTOR_THREAD_FACTORY);
task = evictor.scheduleWithFixedDelay(this, this.keepAliveTime, this.keepAliveTime, TimeUnit.NANOSECONDS);
}
evictorService = evictor;
evictorTask = task;
}
每触发一次线程调用,就会调用一次evictExpiredWorkers()方法。通过遍历expiringWorkerQueue找到已到过期时间的ThreadWorker并将其remove。达到释放无用的ThreadWorker效果。
// IoScheduler.java - class CachedWorkerPool
@Override
public void run() {
evictExpiredWorkers();
}
...
void evictExpiredWorkers() {
if (!expiringWorkerQueue.isEmpty()) {
long currentTimestamp = now();
for (ThreadWorker threadWorker : expiringWorkerQueue) {
if (threadWorker.getExpirationTime() <= currentTimestamp) {
if (expiringWorkerQueue.remove(threadWorker)) {
allWorkers.remove(threadWorker);
}
} else {
// Queue is ordered with the worker that will expire first in the beginning, so when we
// find a non-expired worker we can stop evicting.
break;
}
}
}
}
小结
总的来说:
- ThreadWorker内部持有Java线程池。
- EventLoopWorker负责触发CachedWorkerPool对于ThreadWorker的创建、复用及销毁。
- CachedWorkerPool内部持有ThreadWorker缓存队列,间接管理Java线程池的创建、复用以及销毁。
- 当线程任务执行完后,装饰器DisposeTask会通过触发EventLoopWorker.dispose方法将当前的ThreadWorker加入到CachedWorkerPool的缓存队列,并设置过期时间。
- CachedWorkerPool会通过线程定时触发的机制移除掉已过期的ThreadWorker。
- 若ThreadWorker为达到过期时间,则会在CachedWorkerPool.get()方法中被复用。
ps:有上述的代码分析可知,IoScheduler会在线程资源无法复用的情况下,创建新的线程池以供Rx的线程调度。如果有过多耗时逻辑时可能需要谨慎使用Scheduler.io()。
源码:
AndroidSchedulers.mainThread()
AndroidSchedulers顾名思义就是封装有关Android的scheduler的类。关于Android的线程调度就离不开Handler-Looper-Message那一套体系了,关于这套消息传递机制这里不过多介绍。
以下代码可以看到AndroidSchedulers.mainThread()中返回的Scheduler是一个拥有主线程Looper的HandlerScheduler对象。可以推测后续的线程切换操作,是通过Handler的消息传递实现的。 ps:需要注意的是AndroidSchedulers并不是一个Scheduler类型,它的角色类似Schedulers.io()用于创建和提供Scheduler。
// AndroidSchedulers.java
public final class AndroidSchedulers {
private static final class MainHolder {
static final Scheduler DEFAULT = new HandlerScheduler(new Handler(Looper.getMainLooper()));
}
private static final Scheduler MAIN_THREAD = RxAndroidPlugins.initMainThreadScheduler(
new Callable<Scheduler>() {
@Override public Scheduler call() throws Exception {
return MainHolder.DEFAULT;
}
});
public static Scheduler mainThread() {
return RxAndroidPlugins.onMainThreadScheduler(MAIN_THREAD);
}
...
// HandlerScheduler.java
final class HandlerScheduler extends Scheduler {
private final Handler handler;
HandlerScheduler(Handler handler) {
this.handler = handler;
}
让我们以observeOn为例看看HandlerScheduler的线程调度实现。
Observable.create<String> {
Log.d(TAG, "事件产生线程:${Thread.currentThread().name}")
it.onNext("I'm ")
it.onComplete()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.d(TAG, "事件消费线程:${Thread.currentThread().name}")
Log.d(TAG, it)
}
在订阅期间ObservableObserveOn.subscribeActual会调用scheduler.createWorker(),这个和上述的IoScheduler类似。(这里的scheduler根据上述示例代码可理解为AndroidSchedulers.mainThread()返回的HandlerScheduler)。创建后的Scheduler.Worker对象作为ObserveOnObserver,用于后续观察事件更新的线程调度。
HandlerScheduler.createWorker()会创建一个HandlerWorker对象,这里的handler为拥有主线程Looper的Handler(Handler(Looper.getMainLooper()))。
// ObservableObserveOn.java
@Override
protected void subscribeActual(Observer<? super T> observer) {
if (scheduler instanceof TrampolineScheduler) {
source.subscribe(observer);
} else {
Scheduler.Worker w = scheduler.createWorker();
source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
}
}
// HandlerScheduler.java
@Override
public Worker createWorker() {
return new HandlerWorker(handler);
}
在前文中有提及到,到Rx的事件下发譬如经过observeOn操作符的onNext方法时,会调用ObserveOnObserver.schedule()。worker.schedule(this);及为调用HandlerWorker.schedule。
// ObservableObserveOn.java - class ObserveOnObserver
void schedule() {
if (getAndIncrement() == 0) {
worker.schedule(this);
}
}
HandlerWorker.schedule方法内,将需要执行的Runnable对象利用ScheduledRunnable类装饰,再通过Message的形式利用Handler切换到其所在线程(主线程)执行。
private static final class HandlerWorker extends Worker {
private final Handler handler;
...
@Override
public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
if (run == null) throw new NullPointerException("run == null");
if (unit == null) throw new NullPointerException("unit == null");
if (disposed) {
return Disposables.disposed();
}
run = RxJavaPlugins.onSchedule(run);
ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);
Message message = Message.obtain(handler, scheduled);
message.obj = this; // Used as token for batch disposal of this worker's runnables.
handler.sendMessageDelayed(message, Math.max(0L, unit.toMillis(delay)));
if (disposed) {
handler.removeCallbacks(scheduled);
return Disposables.disposed();
}
return scheduled;
}
...
源码:
总结
最后再总结一下关于Scheduler的通用逻辑:
- Scheduler.Worker的角色为线程管理,RxJava真正封装线程调度的地方。
- Scheduler的角色为调度者。负责管理Scheduler.Worker,包括其创建、复用及销毁。
最后
本文重点解析Schedulers.io()(IoScheduler)与AndroidSchedulers.mainThread()的源码。这里分享一篇对于其他Scheduler介绍比较全的文章:RxJava之Schedulers源码介绍。
系列文章: