讀RxJava原始碼:理解subscribe原理
前言
使用RxJava也有一段時間了,通過這種訂閱資料的思想編寫程式碼,避免了大量的介面回撥,使得資料處理更加方便,對外提供資料的方式更加統一,迴避了同步介面和非同步介面的不同。
本文是閱讀拋物線的《給 Android 開發者的 RxJava 詳解》一文後,結合閱讀原始碼理解觀察、訂閱實現原理的筆記。
最簡單的觀察、訂閱
下面是一個Observable的建立和完成訂閱的示例程式碼
Observable
.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
subscriber.onNext(0);
subscriber.onCompleted();
}
})
.subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
}
});
需要關注的就三個:
- Observable
- OnSubscriber
- Subscriber
Observable
首先看create()如何建立了一個了一個Observable。
public static <T> Observable<T> create(OnSubscribe<T> f) {
return new Observable<T>(RxJavaHooks.onCreate(f));
}
protected Observable(OnSubscribe<T> f) {
this.onSubscribe = f;
}
過程很簡單,就是將傳遞給 create() 的 OnSubscribe 儲存了起來就結束了。RxJavaHooks主要是用於效能優化,在RxJava的原始碼中很常見。
subscribe()
// 核心程式碼
public final Subscription subscribe(Subscriber<? super T> subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber);
return subscriber;
}
通過核心程式碼,可見流程十分簡單,首先是呼叫傳入的 subscriber#onStart 方法,該方法預設不做任何操作。之後就是將Subscriber當作引數呼叫Observable中的OnSubscriber#call,而在 call() 中呼叫了subscriber的 onNext() 和 onCompelte() 。資料就完成了從了Observable.OnSubscribe到Subscriber的資料的傳遞。最後返回的Subscriber是為了方便取消訂閱等操作。
給subscriber新增Subscription
在實際應用中,會有這樣一個需求:在subscriber退訂時需要清理Observable被訂閱時一起建立的資源,例如關閉socket等。示例程式碼如下:
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
// 建立資源
subscriber.add(Subscriptions.create(new Action0() {
@Override
public void call() {
// 在退訂時被呼叫,清理資源
}
}));
// do something
}
});
程式碼中給subscriber新增了一個Subscription,Subscription介面有兩個方法:
- void unsubscribe();
- boolean isUnsubscribed();
void unsubscribe();
在退訂時被呼叫。通過 Subscriptions#create 建立的Subscription會在退訂時呼叫 Action0#call 。實現程式碼如下:
// 構造方法
private BooleanSubscription(Action0 action) {
actionRef = new AtomicReference<Action0>(action);
}
@Override
public boolean isUnsubscribed() {
return actionRef.get() == EMPTY_ACTION;
}
@Override
public void unsubscribe() {
Action0 action = actionRef.get();
if (action != EMPTY_ACTION) {
action = actionRef.getAndSet(EMPTY_ACTION);
if (action != null && action != EMPTY_ACTION) {
action.call();
}
}
}
可見在退訂時會清除對action的引用,並且是通過判斷action是否為空引用來判斷是否已經被退訂的,並且使用了AtomicReference類來儲存引用,保證了該類執行緒安全。
退訂時,Subscription#unsubscribe
被呼叫的原理可以檢視 SubscriptionList 的原始碼知曉:
// subscriber#unsubscribe
@Override
public final void unsubscribe() {
subscriptions.unsubscribe();
}
// SubscriptionList
@Override
public void unsubscribe() {
if (!unsubscribed) {
List<Subscription> list;
synchronized (this) {
if (unsubscribed) {
return;
}
unsubscribed = true;
list = subscriptions;
subscriptions = null;
}
// we will only get here once
unsubscribeFromAll(list);
}
}
private static void unsubscribeFromAll(Collection<Subscription> subscriptions) {
if (subscriptions == null) {
return;
}
List<Throwable> es = null;
for (Subscription s : subscriptions) {
try {
s.unsubscribe();
} catch (Throwable e) {
if (es == null) {
es = new ArrayList<Throwable>();
}
es.add(e);
}
}
Exceptions.throwIfAny(es);
}
核心思想就是退訂時遍歷subscriptions中的Subcription並呼叫 unsubscribe() 。
總結
可見完成一個基本的觀察、訂閱原理並不複雜,而在原始碼中會有很多效能優化,錯誤處理相關的程式碼,在閱讀原始碼時需要學會挑重點、優先關注核心的邏輯程式碼。
相關文章
- RxJava2.x 從原始碼分析原理RxJava原始碼
- TiCDC 原始碼閱讀(四)TiCDC Scheduler 工作原理解析原始碼
- RxJava原始碼初探RxJava原始碼
- RxJava2原始碼解讀之 Map、FlatMapRxJava原始碼
- 小白讀原始碼 | RxJava2 入門篇(一)原始碼RxJava
- Android主流三方庫原始碼分析(五、深入理解RxJava原始碼)Android原始碼RxJava
- RxJava小考題 -- Rxjava原始碼分析(一)RxJava原始碼
- RxJava2原始碼分析(二):操作符原理分析RxJava原始碼
- RxJava 原始碼分析系列(四) -操作符變換原理RxJava原始碼
- MMKV原始碼解讀與理解原始碼
- RxJava + Retrofit原始碼解析RxJava原始碼
- RxJava2.x 分析原始碼,理解操作符FlatMapRxJava原始碼
- Promise原理解讀Promise
- 從路由原理出發,深入閱讀理解react-router 4.0的原始碼路由React原始碼
- MySQL半一致性讀原理解析-從原始碼角度解析MySql原始碼
- 深入原始碼理解Spring整合MyBatis原理原始碼SpringMyBatis
- 10章 RxJava原始碼分析RxJava原始碼
- RxJava2原始碼分析RxJava原始碼
- Layui 原始碼淺讀(模組載入原理)UI原始碼
- Vue原始碼閱讀 – 依賴收集原理Vue原始碼
- 從原始碼解讀Category實現原理原始碼Go
- HashMap 基礎原理以及原始碼解讀HashMap原始碼
- Vue原始碼閱讀 - 依賴收集原理Vue原始碼
- 深入Java原始碼理解執行緒池原理Java原始碼執行緒
- iOS彈幕(原始碼)實現原理解析iOS原始碼
- RxJava2 原始碼解析(一)RxJava原始碼
- RxJava2 原始碼解析(二)RxJava原始碼
- 從koa-session原始碼解讀session原理Session原始碼
- Spring:原始碼解讀Spring IOC原理Spring原始碼
- Vue 原始碼解讀(3)—— 響應式原理Vue原始碼
- 深入RxJava2 原始碼解析(一)RxJava原始碼
- TODO-MVP-RXJAVA 原始碼體驗MVPRxJava原始碼
- 深入RxJava2 原始碼解析(二)RxJava原始碼
- RxJava 原始碼解析之觀察者模式RxJava原始碼模式
- jQuery原始碼閱讀(九)---ready函式理解jQuery原始碼函式
- 友好 RxJava2.x 原始碼解析(三)zip 原始碼分析RxJava原始碼
- 深入原始碼理解Spark RDD的資料分割槽原理原始碼Spark
- Spring MVC原始碼(三) ----- @RequestBody和@ResponseBody原理解析SpringMVC原始碼