讀RxJava原始碼:理解subscribe原理

weixin_34321977發表於2016-11-04

前言

使用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) {

                    }
                });

需要關注的就三個:

  1. Observable
  2. OnSubscriber
  3. 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.OnSubscribeSubscriber的資料的傳遞。最後返回的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介面有兩個方法:

  1. void unsubscribe();
  2. 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()

總結

可見完成一個基本的觀察、訂閱原理並不複雜,而在原始碼中會有很多效能優化,錯誤處理相關的程式碼,在閱讀原始碼時需要學會挑重點、優先關注核心的邏輯程式碼。

相關文章