本篇文章已授權微信公衆號 guolin_blog (郭霖)獨家發佈
RxJava是一個神奇的框架,用法很簡單,但內部實現有點複雜,代碼邏輯有點繞。我讀源碼時,確實有點似懂非懂的感覺。網上關於RxJava源碼分析的文章,源碼貼了一大堆,代碼邏輯繞來繞去的,讓人看得雲裏霧裏的。既然用拆輪子的方式來分析源碼比較難啃,不如換種方式,以造輪子的方式,將源碼中與性能、兼容性、擴展性有關的代碼剔除,留下核心代碼帶大家揭祕RxJava的實現原理。
什麼是響應式編程
首先,我們需要明確,RxJava是Reactive Programming在Java中的一種實現。什麼是響應式編程?
用一個字來概括就是流(Stream)。Stream 就是一個按時間排序的 Events 序列,它可以放射三種不同的 Events:(某種類型的)Value、Error 或者一個” Completed” Signal。通過分別爲 Value、Error、”Completed”定義事件處理函數,我們將會異步地捕獲這些 Events。基於觀察者模式,事件流將從上往下,從訂閱源傳遞到觀察者。
至於使用Rx框架的優點,它可以避免回調嵌套,更優雅地切換線程實現異步處理數據。配合一些操作符,可以讓處理事件流的代碼更加簡潔,邏輯更加清晰。
搭建大體的框架
要造一座房子,首先要把大體的框架搭好。在RxJava裏面,有兩個必不可少的角色:Subscriber(觀察者) 和 Observable(訂閱源)。
Subscriber(觀察者)
Subsribler在RxJava裏面是一個抽象類,它實現了Observer
接口。
public interface Observer<T> {
void onCompleted();
void onError(Throwable t);
void onNext(T var1);
}
爲了儘可能的簡單,將Subscriber簡化如下:
public abstract class Subscriber<T> implements Observer<T> {
public void onStart() {
}
}
Observable(訂閱源)
Observable(訂閱源)在RxJava裏面是一個大而雜的類,擁有很多工廠方法和各式各樣的操作符。每個Observable裏面有一個OnSubscribe
對象,只有一個方法(void call(Subscriber<? super T> subscriber);
),用來產生數據流,這是典型的命令模式。
public class Observable<T> {
final OnSubscribe<T> onSubscribe;
private Observable(OnSubscribe<T> onSubscribe) {
this.onSubscribe = onSubscribe;
}
public static <T> Observable<T> create(OnSubscribe<T> onSubscribe) {
return new Observable<T>(onSubscribe);
}
public void subscribe(Subscriber<? super T> subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber);
}
public interface OnSubscribe<T> {
void call(Subscriber<? super T> subscriber);
}
}
實踐
到此,一個小型的RxJava的雛形就出來了。不信?我們來實踐一下吧。
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
for (int i = 0; i < 10; i++) {
subscriber.onNext(i);
}
}
}).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable t) {
}
@Override
public void onNext(Integer var1) {
System.out.println(var1);
}
});
添加操作符
其實,強大的RxJava的核心原理並沒有想象中那麼複雜和神祕,運用的就是典型的觀察者模式。有了基本雛形之後,我們繼續爲這個框架添磚加瓦吧。RxJava之所以強大好用,與其擁有豐富靈活的操作符是分不開的。那麼我們就試着爲這個框架添加一個最常用的操作符:map。
那麼RxJava是如何實現操作符的呢?其實,每調用一次操作符的方法,就相當於在上層數據源和下層觀察者之間橋接了一個新的Observable。橋接的Observable內部會實例化有新的OnSuscribe和Subscriber。OnSuscribe負責接受目標Subscriber傳來的訂閱請求,並調用源Observable.OnSubscribe的subscribe方法。源Observable.OnSubscribe將Event往下發送給橋接Observable.Subscriber,最終橋接Observable.Subscriber將Event做相應處理後轉發給目標Subscriber。流程如下圖所示:
接着,我們用代碼實現這一過程。在Observable類裏面添加如下代碼:
public <R> Observable<R> map(Transformer<? super T, ? extends R> transformer) {
return create(new OnSubscribe<R>() { // 生成一個橋接的Observable和 OnSubscribe
@Override
public void call(Subscriber<? super R> subscriber) {
Observable.this.subscribe(new Subscriber<T>() { // 訂閱上層的Observable
@Override
public void onCompleted() {
subscriber.onCompleted();
}
@Override
public void onError(Throwable t) {
subscriber.onError(t);
}
@Override
public void onNext(T var1) {
// 將上層的onSubscribe發送過來的Event,通過轉換和處理,轉發給目標的subscriber
subscriber.onNext(transformer.call(var1));
}
});
}
});
}
public interface Transformer<T, R> {
R call(T from);
}
map操作符的作用是將T類型的Event轉化成R類型,轉化策略抽象成Transformer<T, R>
(RxJava中用的是Func1<T, R>
,但爲了便於理解,起了一個有意義的名字)這一個函數接口,由外部傳入。
上面代碼中使用到一些泛型的通配符,有些地方使用了super,有些地方使用了extends,其實這是有講究的,傳給Transformer#call方法的參數是T類型的,那麼call方法的參數類型可以聲明成是T的父類,Transformer#call方法的返回值要求是R類型的,那麼它的返回值類型應該聲明成R的子類。如果大家不能理解,也可以不用在意這些細節。
那麼我們一起來測試一下吧。
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
for (int i = 0; i < 10; i++) {
subscriber.onNext(i);
}
}
}).map(new Observable.Transformer<Integer, String>() {
@Override
public String call(Integer from) {
return "maping " + from;
}
}).subscribe(new Subscriber<String>() {
@Override
public void onNext(String var1) {
System.out.println(var1);
}
@Override
public void onCompleted() {}
@Override
public void onError(Throwable t) {}
});
但是,我們看到map()
方法內內部類有點多,代碼缺少拓展性和可讀性,我們應該進行適當地重構,將主要的邏輯抽離成獨立的模塊,並保證模塊間儘量解耦,否則Observable只會越來越臃腫。
public <R> Observable<R> map(Transformer<? super T, ? extends R> transformer) {
return create(new MapOnSubscribe<T, R>(this, transformer));
}
public class MapOnSubscribe<T, R> implements Observable.OnSubscribe<R> {
final Observable<T> source;
final Observable.Transformer<? super T, ? extends R> transformer;
public MapOnSubscribe(Observable<T> source, Observable.Transformer<? super T, ? extends R> transformer) {
this.source = source;
this.transformer = transformer;
}
@Override
public void call(Subscriber<? super R> subscriber) {
source.subscribe(new MapSubscriber<R, T>(subscriber, transformer));
}
}
public class MapSubscriber<T, R> extends Subscriber<R> {
final Subscriber<? super T> actual;
final Observable.Transformer<? super R, ? extends T> transformer;
public MapSubscriber(Subscriber<? super T> actual, Observable.Transformer<? super R, ? extends T> transformer) {
this.actual = actual;
this.transformer = transformer;
}
@Override
public void onCompleted() {
actual.onCompleted();
}
@Override
public void onError(Throwable t) {
actual.onError(t);
}
@Override
public void onNext(R var1) {
actual.onNext(transformer.call(var1));
}
}
添加線程切換功能
RxJava中最激動人心的功能是異步處理,能夠自如地切換線程。利用 subscribeOn()
結合 observeOn()
來實現線程控制,讓事件的產生和消費發生在不同的線程。 observeOn()
可以多次調用,實現了線程的多次切換,最終目標Subscriber的執行線程與最後一次observeOn()
的調用有關。但subscribeOn()
多次調用只有第一個subscribeOn()
起作用。爲什麼呢?因爲 observeOn()
作用的是Subscriber,而subscribeOn()
作用的是OnSubscribe。
這裏借用扔物線的圖:
簡單地調用一個方法就可以完成線程的切換,很神奇對吧。RxJava是如何實現的呢?除了橋接Observable以外,RxJava還用到一個很關鍵的類—Scheduler(調度器)。文檔中給Scheduler的定義是:A Scheduler is an object that schedules units of work.
,也就是進行任務的調度的一個東西。Scheduler裏面有一個重要的抽象方法:
public abstract Worker createWorker();
Worker是Scheduler的內部類,它是具體任務的執行者。當要提交任務給Worker執行需要調用Worker的schedule(Action0 aciton)
方法。
public abstract Subscription schedule(Action0 action);
要獲得一個Scheduler並不需要我們去new,一般是調用Schedulers的工廠方法。
public final class Schedulers {
private final Scheduler computationScheduler;
private final Scheduler ioScheduler;
private final Scheduler newThreadScheduler;
public static Scheduler io() {
return RxJavaHooks.onIOScheduler(getInstance().ioScheduler);
}
public static Scheduler computation() {
return RxJavaHooks.onComputationScheduler(getInstance().computationScheduler);
}
...
}
具體的Scheduler的實現類就不帶大家一起看了,但我們需要知道,能做到線程切換的關鍵Worker的schedule
方法,因爲它會把傳過來的任務放入線程池,或新線程中執行,這取決於具體Scheduler的實現。
自定義Scheduler
那麼,下面我們先來自定義一個簡單的Scheduler和Worker。
public class Scheduler {
final Executor executor;
public Scheduler(Executor executor) {
this.executor = executor;
}
public Worker createWorker() {
return new Worker(executor);
}
public static class Worker {
final Executor executor;
public Worker(Executor executor) {
this.executor = executor;
}
// 這裏接受的是Runnable而不是Action0,其實這沒什麼關係,主要是懶得自定義函數式接口了。
public void schedule(Runnable runnable) {
executor.execute(runnable);
}
}
}
爲了達到高仿效果,我們也提供相應的工廠方法。
public class Schedulers {
private static final Scheduler ioScheduler = new Scheduler(Executors.newSingleThreadExecutor());
public static Scheduler io() {
return ioScheduler;
}
}
實現subscribeOn
subscribeOn
是作用於上層OnSubscribe的,可以讓OnSubscribe的call方法在新線程中執行。
因此,在Observable類裏面,添加如下代碼:
public Observable<T> subscribeOn(Scheduler scheduler) {
return Observable.create(new OnSubscribe<T>() {
@Override
public void call(Subscriber<? super T> subscriber) {
subscriber.onStart();
// 將事件的生產切換到新的線程。
scheduler.createWorker().schedule(new Runnable() {
@Override
public void run() {
Observable.this.onSubscribe.call(subscriber);
}
});
}
});
}
測試一下:
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
System.out.println("OnSubscribe@ "+Thread.currentThread().getName()); //new Thread
subscriber.onNext(1);
}})
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<Integer>() {
...
@Override
public void onNext(Integer var1) {
System.out.println("Subscriber@ "+Thread.currentThread().getName()); // new Thread
System.out.println(var1);
}
});
實現observeOn
subscribeOn
是作用於下層Subscriber的,需要讓下層Subscriber的事件處理方法放到新線程中執行。
爲此,在Observable類裏面,添加如下代碼:
public Observable<T> observeOn(Scheduler scheduler) {
return Observable.create(new OnSubscribe<T>() {
@Override
public void call(Subscriber<? super T> subscriber) {
subscriber.onStart();
Scheduler.Worker worker = scheduler.createWorker();
Observable.this.onSubscribe.call(new Subscriber<T>() {
@Override
public void onCompleted() {
worker.schedule(new Runnable() {
@Override
public void run() {
subscriber.onCompleted();
}
});
}
@Override
public void onError(Throwable t) {
worker.schedule(new Runnable() {
@Override
public void run() {
subscriber.onError(t);
}
});
}
@Override
public void onNext(T var1) {
worker.schedule(new Runnable() {
@Override
public void run() {
subscriber.onNext(var1);
}
});
}
});
}
});
}
測試一下:
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
System.out.println("OnSubscribe@ " + Thread.currentThread().getName()); // main
subscriber.onNext(1);
}
})
.observeOn(Schedulers.io())
.subscribe(new Subscriber<Integer>() {
...
@Override
public void onNext(Integer var1) {
System.out.println("Subscriber@ " + Thread.currentThread().getName()); // new Thread
System.out.println(var1);
}
});
在Android中切換線程
經過以上實踐,我們終於知道了RxJava線程切換的核心原理了。下面我們順便來看看Android裏面是如何進行線程切換的。
首先找到AndroidSchedulers
,發現一個Scheduler的具體實現類:LooperScheduler。
private AndroidSchedulers() {
...
mainThreadScheduler = new LooperScheduler(Looper.getMainLooper());
...
}
/** A {@link Scheduler} which executes actions on the Android UI thread. */
public static Scheduler mainThread() {
return getInstance().mainThreadScheduler;
}
LooperScheduler的代碼很清晰,內部持有一個Handler,用於線程的切換。在Worker的schedule(Action0 action,...)
方法中,將action通過Handler切換到所綁定的線程中執行。
class LooperScheduler extends Scheduler {
private final Handler handler;
LooperScheduler(Looper looper) {
handler = new Handler(looper);
}
LooperScheduler(Handler handler) {
this.handler = handler;
}
@Override
public Worker createWorker() {
return new HandlerWorker(handler);
}
static class HandlerWorker extends Worker {
private final Handler handler;
...
@Override
public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) {
...
action = hook.onSchedule(action);
ScheduledAction scheduledAction = new ScheduledAction(action, handler);
Message message = Message.obtain(handler, scheduledAction);
message.obj = this; // Used as token for unsubscription operation.
handler.sendMessageDelayed(message, unit.toMillis(delayTime));
...
return scheduledAction;
}
@Override
public Subscription schedule(final Action0 action) {
return schedule(action, 0, TimeUnit.MILLISECONDS);
}
}
static final class ScheduledAction implements Runnable, Subscription {
private final Action0 action;
private final Handler handler;
private volatile boolean unsubscribed;
...
@Override public void run() {
try {
action.call();
} ...
}
...
}
}
結語
就這樣,以上用代碼演示了RxJava一些核心功能是如何實現的,希望能給大家帶來不一樣的啓發。但這只是一個小小的Demo,離真正能運用於工程的Rx框架還差太遠。這也讓我們明白到,一個健壯的框架,需要考慮太多東西,比如代碼的可拓展性和可讀性,性能優化,可測試性,兼容性,極端情況等等。但有時要想深入理解一個複雜框架的實現原理,就需要剝離這些細節代碼,多關注主幹的調用邏輯,化繁爲簡。
Demo代碼可到Github獲取:https://github.com/TellH/RxJavaDemo/tree/master/src/my_rxjava