關於RxJava最友好的文章(進階)

拉丁吳發表於2016-11-08

前言

之前就寫過一篇關於Rxjava最友好的文章,反響很不錯,由於那篇文章的定位就是簡單友好,因此儘可能的摒棄複雜的概念,只抓住關鍵的東西來講,以保證大家都能看懂。

不過那篇文章寫完之後,我就覺得應該還得有一篇文章給RxJava做一個深入的講解才算完美,於是就有了今天的進階篇。因為一個團隊裡可能大家都會用RxJava,但是必須要有一個人很懂這個,不然碰到問題可就麻煩了。

在前一篇文章中的最後,我們得出結論:RxJava就是在觀察者模式的骨架下,通過豐富的操作符和便捷的非同步操作來完成對於複雜業務的處理。今天我們還是就結論中的觀察者模式操作符來做深入的擴充。

在進入正題之前,還是希望大家先去看看關於Rxjava最友好的文章

關於觀察者模式

前一篇文章首先就重點談到了觀察者模式,我們認為觀察者模式RxJava的骨架*。在這裡不是要推翻之前的結論,而是希望從深入它的內部的去了解它的實現。

依然使用之前文章中關於開關和檯燈的程式碼

//建立一個被觀察者(開關)
 Observable switcher=Observable.create(new Observable.OnSubscribe<String>(){

            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("On");
                subscriber.onNext("Off");
                subscriber.onNext("On");
                subscriber.onNext("On");
                subscriber.onCompleted();
            }
        });
//建立一個觀察者(檯燈)
 Subscriber light=new Subscriber<String>() {
            @Override
            public void onCompleted() {
                //被觀察者的onCompleted()事件會走到這裡;
                Log.d("DDDDDD","結束觀察...\n");
            }

            @Override
            public void onError(Throwable e) {
                    //出現錯誤會呼叫這個方法
            }
            @Override
            public void onNext(String s) {
                //處理傳過來的onNext事件
                Log.d("DDDDD","handle this---"+s)
            }
//訂閱
switcher.subscribe(light);複製程式碼

以上就是一個RxJava觀察者架構,
看到這樣的程式碼不知道你會不會有一些疑惑:

  • 被觀察者中的Observable.OnSubscribe是什麼,有什麼用?
  • call(subscriber)方法中,subscriber哪裡來的?
  • 為什麼只有在訂閱之後,被觀察者才會開始傳送訊息?

其實,這些問題都可以通過了解OnSubscribe來解決。

那我們先來看看關於OnSubscribe的定義

//上一篇文章也提到Acton1這個介面,內部只有一個待實現call()方法
//沒啥特別,人畜無害
public interface Action1<T> extends Action {
    void call(T t);
}
//OnSubscribe繼承了這個Action1介面
public interface OnSubscribe<T> extends Action1<Subscriber<? super T>> {
        // OnSubscribe仍然是個介面
    }複製程式碼

那麼也就是說,OnSubscribe本質上也是和 Action1一樣的介面,只不過它專門用於Observable內部。

而在Observable觀察者的類中,OnSubscribe是它唯一的屬性,同時也是Observable建構函式中唯一必須傳入的引數,也就是說,只要建立了Observable,那麼內部也一定有一個OnSubscribe物件。

當然,Observable是沒有辦法直接new的,我們只能通過create(),just()等等方法建立,當然,這些方法背後去呼叫了new Observable(onSubscribe)

public class Observable<T> {
    //唯一的屬性
    final OnSubscribe<T> onSubscribe;
    //建構函式,因為protected,我們只能使用create函式
    protected Observable(OnSubscribe<T> f) {
        this.onSubscribe = f;
    }
    //create(onSubscribe) 內部呼叫建構函式。
    public static <T> Observable<T> create(OnSubscribe<T> f) {
        return new Observable<T>(RxJavaHooks.onCreate(f));
    }
    ....
    ....
    }複製程式碼

當建立了Observable和Subscribe之後,呼叫subscribe(subscriber)方法時,發生了什麼呢?

    //傳入了觀察者物件
    public final Subscription subscribe(final Observer<? super T> observer) {
       ....
        //往下呼叫
        return subscribe(new ObserverSubscriber<T>(observer));
    }

    public final Subscription subscribe(Subscriber<? super T> subscriber) {
        //往下呼叫
        return Observable.subscribe(subscriber, this);
    }


    //呼叫到這個函式
 static <T> Subscription subscribe(Subscriber<? super T> subscriber, Observable<T> observable) {
        // new Subscriber so onStart it
        subscriber.onStart();

        // add a significant depth to already huge call stacks.
        try {
            // 在這裡簡單講,對onSubscribe進行封裝,不必緊張。
            OnSubscribe onSubscribe=RxJavaHooks.onObservableStart(observable, observable.onSubscribe);

            //這個才是重點!!!
            //這個呼叫的具體實現方法就是我們建立觀察者時
            //寫在Observable.create()中的call()呀
            //而呼叫了那個call(),就意味著事件開始傳送了
            onSubscribe.call(subscriber);
            //不信你往回看

            return RxJavaHooks.onObservableReturn(subscriber);
        } catch (Throwable e) {
            ....
            ....
            }
            return Subscriptions.unsubscribed();
        }
    }複製程式碼

程式碼看到這裡,我們就可以對上面三個問題做統一的回答了:

  • onSubscribe是Observable內部唯一屬性,是連線Observable和subscriber的關鍵,相當於連線檯燈和開關的那根電線
  • call(Subscriber<? super String> subscriber)中的subscriber,就是我們自己建立的那個觀察者
  • 只有在訂閱的時候,才會發生onSubscribe.call(subscriber),進而才會開始呼叫onNext(),onComplete()等。

到這裡,你是不是對於RxJava的觀察者模式瞭解更加清晰了呢?我們用流程圖複習一下剛才的過程。

關於RxJava最友好的文章(進階)

瞭解了上面這些,我們就可以更進一步做以下總結:

  • 訂閱這個動作,實際上是觀察者(subscriber)物件把自己傳遞給被觀察者(observable)內部的onSubscribe
  • onSubscribe的工作就是呼叫call(subscriber)來通知被觀察者傳送訊息給這個subscriber

以上的結論對於下面我們理解操作符的原理十分有幫助,因此一定要看明白。

觀察者模式介紹到這裡,才敢說講完了。

關於操作符

上一篇文章講了一些操作符,並且在github上放了很多其他的操作符使用範例給大家,因此在這裡不會介紹更多操作符的用法,而是講解操作符的實現原理。他是如何攔截事件,然後變換處理之後,最後傳遞到觀察者手中的呢?

相信瞭解相關內容的人可能會想到lift()操作符,它本來是其他操作符做變換的基礎,不過那已經是好幾個版本以前的事情了。但是目前版本中RxJava已經不一樣了了,直接把lift()的工作下放到每個操作符中,把lift的弱化了(但是依然保留了lift()操作符)。

因此,我們在這裡不必講解lift,直接拿一個操作符做例子,來了解它的原理即可,因為基本上操作符的實現原理都是一樣的。

以map()為例,依然拿之前文章裡面的例子:

 Observable.just(getFilePath())
            //使用map操作來完成型別轉換
            .map(new Func1<String, Bitmap>() {
              @Override
              public Bitmap call(String s) {
                //顯然自定義的createBitmapFromPath(s)方法,是一個極其耗時的操作
                  return createBitmapFromPath(s);
              }
          })
            .subscribe(
                 //建立觀察者,作為事件傳遞的終點處理事件    
                  new Subscriber<Bitmap>() {
                        @Override
                        public void onCompleted() {
                            Log.d("DDDDDD","結束觀察...\n");
                        }

                        @Override
                        public void onError(Throwable e) {
                            //出現錯誤會呼叫這個方法
                        }
                        @Override
                        public void onNext(Bitmap s) {
                            //處理事件
                            showBitmap(s)
                        }
                    );複製程式碼

看看map背後到底做了什麼

 public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
            //建立了全新代理的的Observable,建構函式傳入的引數是OnSubscribe
            //OnSubscribeMap顯然是OnSubscribe的一個實現類,
            //也就是說,OnSubscribeMap需要實現call()方法
            //建構函式傳入了真實的Observable物件
            //和一個開發者自己實現的Func1的例項
        return create(new OnSubscribeMap<T, R>(this, func));
    }複製程式碼

看OnSubscribeMap的具體實現:

public final class OnSubscribeMap<T, R> implements OnSubscribe<R> {
    //用於儲存真實的Observable物件
    final Observable<T> source;
    //還有我們傳入的那個Func1的例項
    final Func1<? super T, ? extends R> transformer;

    public OnSubscribeMap(Observable<T> source, Func1<? super T, ? extends R> transformer) {
        this.source = source;
        this.transformer = transformer;
    }

    //實現了call方法,我們知道call方法傳入的Subscriber
    //就是訂閱之後,外部傳入真實的的觀察者
    @Override
    public void call(final Subscriber<? super R> o) {
        //把外部傳入的真實觀察者傳入到MapSubscriber,構造一個代理的觀察者
        MapSubscriber<T, R> parent = new MapSubscriber<T, R>(o, transformer);
        o.add(parent);
        //讓外部的Observable去訂閱這個代理的觀察者
        source.unsafeSubscribe(parent);
    }

    //Subscriber的子類,用於構造一個代理的觀察者
    static final class MapSubscriber<T, R> extends Subscriber<T> {
            //這個Subscriber儲存了真實的觀察者
        final Subscriber<? super R> actual;
        //我們自己在外部自己定義的Func1
        final Func1<? super T, ? extends R> mapper;

        boolean done;

        public MapSubscriber(Subscriber<? super R> actual, Func1<? super T, ? extends R> mapper) {
            this.actual = actual;
            this.mapper = mapper;
        }
        //外部的Observable傳送的onNext()等事件
        //都會首先傳遞到代理觀察者這裡
        @Override
        public void onNext(T t) {
            R result;

            try {
                //mapper其實就是開發者自己建立的Func1,
                //call()開始變換資料
                result = mapper.call(t);
            } catch (Throwable ex) {
                Exceptions.throwIfFatal(ex);
                unsubscribe();
                onError(OnErrorThrowable.addValueAsLastCause(ex, t));
                return;
            }
            //呼叫真實的觀察者的onNext()
            //從而在變換資料之後,把資料送到真實的觀察者手中
            actual.onNext(result);
        }
        //onError()方法也是一樣
        @Override
        public void onError(Throwable e) {
            if (done) {
                RxJavaHooks.onError(e);
                return;
            }
            done = true;

            actual.onError(e);
        }


        @Override
        public void onCompleted() {
            if (done) {
                return;
            }
            actual.onCompleted();
        }

        @Override
        public void setProducer(Producer p) {
            actual.setProducer(p);
        }
    }
}複製程式碼

map操作符的原理基本上就講完了,其他的操作符和map在原理上是一致的。

假如你想建立自定義的操作符(這其實是不建議的),你應該按照上面的步驟

  • 建立一個代理的被觀察者
  • 實現被觀察者中onSubscribe的call方法
  • 在call方法中建立一個代理的觀察者,讓真實的被觀察者訂閱它。

我知道你會有點暈,沒關係,我後面會寫一個自定義操作符放在我的github上,可以關注下。

下面,我們先通過一個流程圖鞏固一下前面學習的成果。

關於RxJava最友好的文章(進階)

下次你使用操作符時,心裡應該清楚,每使用一個操作符,都會建立一個代理觀察者和一個代理被觀察者,以及他們之間是如何相互呼叫的。相信有了這一層的理解,今後使用RxJava應該會更加得心應手。

不過最後,我想給大家留一個思考題:使用一個操作符時,流程圖是這樣的,那麼使用多個操作符呢?

勘誤

暫無

後記

到這裡,關於RxJava的講解就基本可以告一段落了,

我相信,兩篇文章讀下來,對於RxJava的理解應該已經到了比較高的一個層次了,我的目標也就達到了。

接下來....

因為RxJava是一個事件的非同步處理框架,理論上,他可以封裝任何其他的庫,那麼.....

相關文章