RxJava簡單介紹
RxJava現在幾乎每個Android項目中都有它的身影,RxJava是使用了Rx(ReactiveX)函數庫的語言庫,Rx是一個函數庫,讓開發者可以利用可觀察序列和LINQ風格查詢操作符來編寫異步和基於事件的程序,除了RxJava,Rx幾乎支持了全部的流行編程語言,例如:RxJS、Rx.NET等等。
RxJava的優點和使用方法這篇文章不做介紹,今天關注一下框架內部原理:比如我們平時用RxJava的時候會使用subscribeOn(Schedulers.io())和observeOn(AndroidSchedulers.mainThread())來切換線程,有沒有對其內部的實現感興趣呢?
讀這篇文章之前可以先看看這篇,有助於理解流程關係 《碼上理解— 手撕RxJava訂閱關係,事件發送和接收》
文章目的
通過看源碼的實現過程,來理解RxJava在Android平臺上如何通過subscribeOn(Schedulers.io())方法實現線程調度的
環境搭建和代碼示例
示例是基於RxJava2
配置和代碼很簡單,如下
implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
public class MainActivity extends AppCompatActivity {
private Disposable disposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rxJava();
}
public void rxJava() {
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
e.onNext("事件1");
}
})
.subscribeOn(Schedulers.io())
//.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.i("RxJava",s+","+Thread.currentThread().getName());
disposable = d;
}
@Override
public void onNext(String s) {
Log.i("RxJava",s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (disposable != null)
if (!disposable.isDisposed())
disposable.dispose();
}
}
運行後日志打印
I/RxJava: 訂閱
I/RxJava: 事件1,RxCachedThreadScheduler-1
文章有點長,可以先拉到底部,先看總結,然後再一步一步跟代碼
分析subscribeOn(Schedulers.io())
我們先嚐試看一下subscribeOn(Schedulers.io())這個方法
點擊subscribe方法進入源碼
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable<T> subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}
發現進入了抽象類Observable的subscribeOn方法,方法的參數是Scheduler 類型,Scheduler的翻譯是調度器,進一步跟蹤Scheduler類,發現Scheduler類也是抽象類,以下是部分代碼,可以先放上混個眼熟
public abstract class Scheduler {
... ...
@NonNull
public abstract Worker createWorker();
... ...
@NonNull
public Disposable scheduleDirect(@NonNull Runnable run) {
return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
}
... ...
public abstract static class Worker implements Disposable {
@NonNull
public Disposable schedule(@NonNull Runnable run) {
return schedule(run, 0L, TimeUnit.NANOSECONDS);
}
@NonNull
public abstract Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit);
}
... ...
}
由於Schedulers.io()就是方法實參,所以我們推斷Schedulers.io()就是Scheduler的實現類,換句話說Schedulers.io()就是調度器,那我們先看以下這個調度器是什麼吧!
Schedulers.io()
我們嘗試點擊Schedulers類,跳轉源碼,發現用到了策略模式,Schedulers.io()是其中一個策略
常見的策略的使用場景瞭解一下
1.Schedulers.io(): I/O 操作(讀寫文件、讀寫數據庫、網絡信息交互等)所使用的 Scheduler。行爲模式和 newThread() 差不多,區別在於 io() 的內部實現是是用一個無數量上限的線程池,可以重用空閒的線程,因此多數情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中,可以避免創建不必要的線程。
2.Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操作限制性能的操作,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小爲 CPU 核數。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU。
3.另外, Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行。
我們看一下Schedulers.io()具體的實現方式
@NonNull
public static Scheduler io() {
return RxJavaPlugins.onIoScheduler(IO);
}
發現返回了RxJavaPlugins.onIoScheduler,這個類RxJavaPlugins下的方法其實是實現hook,由於我們當前不關心hook,先不用管它,它方法內返回的其實就是IO對象
/**
* Calls the associated hook function.
* @param defaultScheduler the hook's input value
* @return the value returned by the hook
*/
@NonNull
public static Scheduler onIoScheduler(@NonNull Scheduler defaultScheduler) {
Function<? super Scheduler, ? extends Scheduler> f = onIoHandler;
if (f == null) {
return defaultScheduler;
}
return apply(f, defaultScheduler);
}
ok,現在我們看一下IO是哪來的,發現其實在類中的靜態代碼塊中初始化的
static {
SINGLE = RxJavaPlugins.initSingleScheduler(new SingleTask());
COMPUTATION = RxJavaPlugins.initComputationScheduler(new ComputationTask());
//這裏初始化了調度器IO
IO = RxJavaPlugins.initIoScheduler(new IOTask());
TRAMPOLINE = TrampolineScheduler.instance();
NEW_THREAD = RxJavaPlugins.initNewThreadScheduler(new NewThreadTask());
}
又看到了RxJavaPlugins類,我們先不去管它,直接看new IOTask()
static final class IOTask implements Callable<Scheduler> {
@Override
public Scheduler call() throws Exception {
return IoHolder.DEFAULT;
}
}
發現他是一個任務,返回值是Scheduler 調度器,那它是從哪裏執行這個任務的呢,我們這時可以看RxJavaPlugins.initNewThreadScheduler這個方法了
@NonNull
public static Scheduler initIoScheduler(@NonNull Callable<Scheduler> defaultScheduler) {
ObjectHelper.requireNonNull(defaultScheduler, "Scheduler Callable can't be null");
//代碼中如果沒有實現onInitIoHandler,這裏始終是null的
Function<? super Callable<Scheduler>, ? extends Scheduler> f = onInitIoHandler;
if (f == null) {
return callRequireNonNull(defaultScheduler);
}
return applyRequireNonNull(f, defaultScheduler);
}
我們傳入了Callable,返回的是Scheduler ,說明在這個方法中執行了IOTask 任務,於是我們進一步看方法callRequireNonNull(defaultScheduler)。
這裏還要說明一下,如果我們沒有自己實現hook方法setInitIoSchedulerHandler(), onInitIoHandler這裏始終是null,所以我們直接看一個參數的方法callRequireNonNull
@NonNull
static Scheduler callRequireNonNull(@NonNull Callable<Scheduler> s) {
try {
return ObjectHelper.requireNonNull(s.call(), "Scheduler Callable result can't be null");
} catch (Throwable ex) {
throw ExceptionHelper.wrapOrThrow(ex);
}
}
ok,我們發現s.call(),執行了IOTask任務。我們再回到IOTask,我們看看到底返回的調度器是個什麼東東
static final class IoHolder {
static final Scheduler DEFAULT = new IoScheduler();
}
發現IoHolder 其實是Schedulers的靜態內部類,靜態內部類中又創建了靜態對象IoScheduler,看一下
IoScheduler
/**
* Scheduler that creates and caches a set of thread pools and reuses them if possible.
*/
public final class IoScheduler extends Scheduler {
static {
...
//工作線程的線程工廠
WORKER_THREAD_FACTORY = new RxThreadFactory(WORKER_THREAD_NAME_PREFIX, priority);
//線程池
NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY);
NONE.shutdown();
}
//無參構造
public IoScheduler() {
this(WORKER_THREAD_FACTORY);
}
//傳入了用於創建工作線程的線程工廠
public IoScheduler(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
this.pool = new AtomicReference<CachedWorkerPool>(NONE);
start();
}
public IoScheduler() {
this(WORKER_THREAD_FACTORY);
}
// 實現了 Scheduler 的createWorker方法
@NonNull
@Override
public Worker createWorker() {
return new EventLoopWorker(pool.get());
}
...
//實現了Scheduler.Worker 抽象類
static final class EventLoopWorker extends Scheduler.Worker {
...
@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);
}
...
}
}
上面的代碼我們可以看出,調度器裏其實是創建了線程池,所以,此處可以推測.subscribeOn(Schedulers.io())方法將每一個事件當作任務放入線程池執行,從而達到線程血環的目的。
再注意一點,IO調度器IoScheduler類中實現了一個抽象方法,一個抽象類,還記得文章一開始混眼熟的那個類嗎,ok,我們再眼熟一下因爲一會要用
到目前爲止,我們知道Schedulers.io()其實就是創建了IoScheduler,它的本質是一個線程池,由方法subscribeOn(Schedulers.io()),IoScheduler對象丟給了subscribeOn方法,那我們進一步看一下subscribeOn方法中究竟是賣的什麼藥
subscribeOn()
subscribeOn我們接着看
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable<T> subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}
前面如果都跟者看過來的話,我們就可以直接進入ObservableSubscribeOn類了(RxJavaPlugins.onAssembly()方法返回的就是ObservableSubscribeOn對象)
ObservableSubscribeOn類我全粘過來了,代碼比較長
public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
final Scheduler scheduler;//IoScheduler
public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
super(source);
this.scheduler = scheduler;
}
@Override
public void subscribeActual(final Observer<? super T> s) {
//包裝一層,將觀察者Observer放入SubscribeOnObserver對象中
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
//調用觀察者Observer的onSubscribe方法
s.onSubscribe(parent);
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
static final class SubscribeOnObserver<T> extends AtomicReference<Disposable> implements Observer<T>, Disposable {
private static final long serialVersionUID = 8094547886072529208L;
final Observer<? super T> actual;
final AtomicReference<Disposable> s;
SubscribeOnObserver(Observer<? super T> actual) {
this.actual = actual;
this.s = new AtomicReference<Disposable>();
}
@Override
public void onSubscribe(Disposable s) {
DisposableHelper.setOnce(this.s, s);
}
@Override
public void onNext(T t) {
actual.onNext(t);
}
@Override
public void onError(Throwable t) {
actual.onError(t);
}
@Override
public void onComplete() {
actual.onComplete();
}
@Override
public void dispose() {
DisposableHelper.dispose(s);
DisposableHelper.dispose(this);
}
@Override
public boolean isDisposed() {
return DisposableHelper.isDisposed(get());
}
void setDisposable(Disposable d) {
DisposableHelper.setOnce(this, d);
}
}
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent;
SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
}
@Override
public void run() {
source.subscribe(parent);
}
}
}
拿到這個類,我們可以知道ObservableSubscribeOn對象中有傳入的IoScheduler調度器,有一個subscribeActual方法,有一個SubscribeOnObserver靜態內部類,還有一個訂閱任務SubscribeTask。
如果看過我另一篇文章的碼上理解— 手撕RxJava訂閱關係,事件發送和接收
應該知道,subscribeActual這個方法其實是由下游subscribe方法調用,也就是執行subscribe就執行subscribeActual
那重點就是subscribeActual方法,看一下subscribeActual方法幹了些什麼
@Override
public void subscribeActual(final Observer<? super T> s) {
//包裝一層,將觀察者Observer放入SubscribeOnObserver對象中
final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
//調用觀察者Observer的onSubscribe方法
s.onSubscribe(parent);
//最後創建了一個任務SubscribeTask,把任務丟給IoScheduler調度器
parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}
先把下游的Observer對象包裝進SubscribeOnObserver對象,然後執行了observer的onSubscribe方法,表示訂閱完成,最後創建了一個任務SubscribeTask,把任務丟給IoScheduler調度器
那IoScheduler的scheduleDirect方法具體幹了些什麼呢,還記得文章開頭混臉熟的Scheduler類嗎?
scheduler.scheduleDirect實際上就是調用到這裏了,我們再看一下三個參數的scheduleDirect方法
public abstract class Scheduler {
@NonNull
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
//還記得IoScheduler 的createWorker方法嗎
final Worker w = createWorker();
// SubscribeTask
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
// 給SubscribeTask包裝一層DisposeTask,實現了Disposable接口
DisposeTask task = new DisposeTask(decoratedRun, w);
// 執行 EventLoopWorker的schedule方法
w.schedule(task, delay, unit);
return task;
}
}
上面註釋已經解釋了,scheduleDirect方法就是將SubscribeTask丟給了schedule方法,那看一下schedule方法具體幹了啥
方法中線程池executor執行了SubscribeTask任務,我們再看一下SubscribeTask的run方法中執行的是什麼
public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
final Scheduler scheduler;
public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
super(source);
this.scheduler = scheduler;
}
...
...
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent;
SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
}
@Override
public void run() {
//執行了上游的subscribe方法
source.subscribe(parent);
}
}
}
run方法中調用了source的subscribe方法。source是SubscribeOnObserver構造方法傳入進來的,它其實就是Observable
那source.subscribe就是調用的Observable的subscribe方法,ok,找一下subscribe
如果看過我另一篇文章的碼上理解— 手撕RxJava訂閱關係,事件發送和接收的老鐵應該知道,subscribeActual是抽象方法,subscribeActual(observer)是跳轉到哪裏,沒錯是Observable.create創建出的ObservableCreate對象裏的subscribeActual方法。
還記得subscribeActual(observer)的參數observer是什麼嗎,是被SubscribeOnObserver包裝的觀察者Observer,來看一下ObservableCreate對象中的subscribeActual方法做了什麼
上圖中爲什麼說e.onNext方法執行在子線程中呢?
因爲下游執行subscribe方法的時候,
1.先將Observer觀察者包裝到SubscribeOnObserver中去(裝飾模式)
2.然後執行了Observer的onSubscribe回調,訂閱完成
3.創建一個任務SubscribeTask,將裝有觀察者Observer的包裹SubscribeOnObserver傳給這個任務,方便在run方法中將包裹SubscribeOnObserver傳給上游,上游拿到包裹可以再進行添加包裹或者解開包裹執行Observer的onNext回調或其它回調
4.將任務SubscribeTask丟進IO調度器IoScheduler中的線程池並submit執行
5.SubscribeTask中的 source.subscribe(parent);將包裹SubscribeOnObserver傳給上游,由於此時已經運行在線程池中了,所以上游的subscribeActual方法是運行在子線程中,ObservableOnSubscribe的subscribe回調是在subscribeActual方法中調用的,所以subscribe方法也是運行在子線程,e.onNext在方法subscribe裏調用,所以e.onNext方法執行也在子線程中。沒毛病。
總結
RxJava通過subscribeOn(Schedulers.io())切換子線程的原理是將subscribeOn的上游都放入線程池中執行;從這裏也可以推斷出另一個結論,當有多個subscribeOn的時候,只有上游的第一個subscribeOn有效,換句話說程序會運行在上游第一個subscribeOn()所切換的線程裏。
最後來張圖總結一下