RxJava 知識梳理(1) RxJava 基本思想

澤毛發表於2017-12-21

一、基礎概述

RxJava的關鍵是非同步,即使隨著程式的邏輯變得複雜,它依然能夠保持簡潔。

二、API介紹和原理剖析

觀察者模式面向的需求是:A物件(觀察者)對B物件(被觀察者)的某種變化高度敏感,需要在B變化的一瞬間做出反應,觀察者採用註冊Register或者訂閱Subscribe的方式,告訴觀察者,我需要你的某某狀態,並在它變化的時候通知我,在RxJava當中,Observable是被觀察者,Observer就是觀察者。

RxJava有四個基本概念:

  • Observable:被觀察者。
  • Observer:觀察者。
  • Subscribe:訂閱。
  • Event:事件。

ObservableObserver通過subscribe方法實現訂閱關係,Observable可以在需要的時候發出事件來通知Observer

RxJava有以下三種事件:

  • onNext:普通事件。
  • onCompletedRxJava不僅把每個事件單獨處理,還會把它們看作一個佇列,當不會再有新的onNext事件發出時,需要觸發onCompleted事件作為標誌。
  • onErroronCompleted和有且僅有一個,並且是事件序列中的最後一個。

三、基本實現

RxJava的基本實現有以下三點: 1)建立觀察者 - Observer

Observer<String> observer = new Observer<String>() {

    @Override
    public void onCompleted() {
        Log.d(TAG, "onCompleted");
    }

    @Override
    public void onError(Throwable e) {
        Log.d(TAG, "onError");
    }

    @Override
    public void onNext(String s) {
        Log.d(TAG, "onNext");
    }
};
複製程式碼

除了Observer介面之外,RxJava還內建了一個實現了Observer的抽象類:Subscriber,它對Observer介面進行了一些擴充套件,實質上在RxJavasubscribe過程中,Observer也總是被轉換成為一個Subscriber再使用,他們的區別在與:

  • onStart:這是新增的方法,它會在subscribe剛開始,而事件還未傳送之前被呼叫,它總是在subscribe所發生的執行緒被呼叫。
  • unsubscribe:這是它實現的另一個介面Subscription的方法,用於取消訂閱,在這個方法被呼叫後,Subscriber將不再接收事件,一般在呼叫這個方法前,可以使用isUnsubscribed判斷一下狀態,Observable在訂閱之後會持有Subscriber的引用,因此不釋放會有記憶體洩漏的危險。

2)建立被觀察者 - Observable RxJavacreate方法來建立一個observable

rx.Observable observable = rx.Observable.create(new rx.Observable.OnSubscribe<String>() {

    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("Hello World!");
        subscriber.onCompleted();
    }
});
複製程式碼

這裡傳入了一個Observable.OnSubscribe<T>物件作為引數,它會被儲存在返回的Observable物件當中,它的作用相當於一個計劃表,當Observable被訂閱的時候,OnSubscribecall方法會自動被呼叫,事件序列被依次觸發。 createRxJava最基本的創造事件序列的方法,基於這個方法,還提供了一些快捷方法來建立事件佇列:

  • just(T...)
Observable observable = Observable.just("Hello", "Hi", "Aloha");
複製程式碼
  • from(T[]) / from(Iterable<? extends T>)
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
複製程式碼

3)訂閱 - subscribe

observable.subscribe(observer);
observable.subscribe(subscriber);
複製程式碼

其內部核心的程式碼類似於:

public Subscription subscribe(Subscriber subscriber) {
    //準備方法。
    subscriber.onStart();
    //事件傳送的邏輯開始執行,這個onSubscribe就是建立Observable時新建的OnSubscribe物件。
    onSubscribe.call(subscriber);
    //把傳入的Subscriber轉換為Subscription並返回,方便unsubscribe。
    return subscriber;
}
複製程式碼

Observable.subscribe方法除了支援傳入ObserverSubscriber,還支援傳入Action0Action1這樣不完整定義的回撥,RxJava會自動根據定義建立出Subscriber

四、執行緒控制

在不指定執行緒的情況下,RxJava遵循這樣的原則,在哪個執行緒呼叫subscribe,就在哪個執行緒產生事件,在哪個執行緒產生事件,就在哪個執行緒消費事件,如果需要消費執行緒,那麼就需要用到SchedulerRxJava內建了幾個Scheduler

  • Schedulers.immediate:直接在當前執行緒執行。
  • Schedulers.newThread:總是啟用新執行緒,並線上程執行操作。
  • Schedulers.io:其內部實現是一個無數量上限的的執行緒池,可以重用空閒的執行緒,不要把計算工作放在io,可以避免建立不必要的執行緒。
  • Schedulers.computation:使用固定的執行緒池,大小為CPU核數。
  • AndroidSchedulers.mainThread:指定的操作將在Android主執行緒中執行。

對執行緒控制有以下兩個方法:

  • subscribeOn:指定subscribe發生的執行緒,即Observable.OnSubscribe被啟用時所處的執行緒,也就是call方法執行時所處的執行緒。
  • observeOn:指定Subscriber所執行在的執行緒。

observeOn指定的是Subscriber的執行緒,而這個Subscriber並不一定是subscribe()引數中的Subscriber,而是observeOn執行時的當前Observable所對應的Subscriber,即它的直接下級Subscriber,也就是它之後的操作所在的執行緒,因此,如果有多次切換執行緒的要求,只要在每個想要切換執行緒的位置呼叫依次observeOn即可。 和observeOn不同,subscribeOn只能呼叫一次,下面我們來分析一下它的內部實現,首先是subscribeOn的原理: subscribeOnObserveOn都做了執行緒切換的工作:

  • subscribeOn的執行緒切換髮生在OnSubscribe中,即在它通知上一級OnSubscribe時,這時事件還沒有傳送,因此subscribeOn的執行緒控制可以從事件發出的開端造成影響。

Paste_Image.png

  • observeOn的執行緒切換則發生在它內建的Subscriber中,即發生在它即將給下一級Subscriber傳送事件時,因此控制的是它後面的執行緒。

Paste_Image.png

五、變換

變換,就是將事件序列中的物件或整個序列進行加工處理,轉換不同的事件或者序列。

5.1 map()

通過FuncX,把引數中的Integer轉換成為String,是最常用的變換,這個變換是發生在subscribeOn所指定的執行緒當中的。

Subscriber<String> subscriber = new Subscriber<String>() {

    @Override
    public void onCompleted() {

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onNext(String s) {
        long nextId = Thread.currentThread().getId();
        Log.d(TAG, "onNext:" + s + ", threadId=" + nextId);
    }
};
Observable<Integer> observable = Observable.create(new Observable.OnSubscribe<Integer>() {
    @Override
    public void call(Subscriber<? super Integer> subscriber) {
        long callId = Thread.currentThread().getId();
        subscriber.onNext(5);
        subscriber.onCompleted();
    }
});
observable.map(new Func1<Integer, String>() {

    @Override
    public String call(Integer integer) {
        long mapId = Thread.currentThread().getId();
        return "My Number is:" + integer;
    }
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
複製程式碼

其示意圖類似於:

Paste_Image.png

5.2 flatMap

它和map有一個共同點,就是把傳入的引數轉化之後返回另一個物件,但是和map不同的是,flatMap返回的是一個Observable物件,而且它並不直接把這個物件傳給Subscriber,而是通過這個新建的Observable來傳送事件,其整個的呼叫過程:

  • 使用傳入的事件物件建立一個Observable
  • 啟用這個Observable,通過它來傳送事件。
  • 每一個建立出來的Observable傳送的事件,被匯入同一個Observable,它複雜將這些事件同一交給Subscriber的回撥方法。
Subscriber<String> subscriber = new Subscriber<String>() {

    @Override
    public void onCompleted() {}

    @Override
    public void onError(Throwable e) {}

    @Override
    public void onNext(String s) {
        Log.d(TAG, "onNext, s=" + s);
    }
};
Observable<List<String>> observable = Observable.create(new Observable.OnSubscribe<List<String>>() {

    @Override
    public void call(Subscriber<? super List<String>> subscriber) {
        List<String> list = new ArrayList<>();
        list.add("First");
        list.add("Second");
        list.add("Third");
        subscriber.onNext(list);
    }
});
observable.flatMap(new Func1<List<String>, Observable<String>>() {
    @Override
    public Observable<String> call(List<String> strings) {
        return Observable.from(strings);
    }
}).subscribe(subscriber);
複製程式碼

其示意圖:

Paste_Image.png

六、變換的原理

變換的實質是針對事件序列的處理和再傳送,在RxJava的內部,它們是基於同一個基礎的變換方法lift(operator)

//生成了一個新的Observable並返回。
public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
    //構造新的Observable時,同時新建了一個OnSubscribe物件。
    return Observable.create(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber subscriber) {
            Subscriber newSubscriber = operator.call(subscriber);
            newSubscriber.onStart();
            //原始的onSubscribe。
            onSubscribe.call(newSubscriber);
        }
    });
}
複製程式碼

示意圖:

Paste_Image.png

  • lift建立了一個Observable後,加上之前的原始Observable,有兩個Observable
  • 新的Observable裡的OnSubscribe加上原始的,共有兩個OnSubscribe
  • 當使用者通過呼叫lift/map建立的Observable物件的subscribe方法時,於是它觸發了上面的call方法中的內容。
  • 在這個新的OnSubscribecall方法中,傳入了目標的Subscriber,同時其外部類中還持有了原始的OnSubscribe。我們先通過operator.call(oldSubscriber)方法,生成了新的Subscriber(new Subscriber),然後利用這個新的Subscriber向原始的Observable進行訂閱。

下面我們以前面map實現的例子來分析一下原始碼,上面的例子通過map操作符把Integer型別的ObservableString型別的Subscriber生成了訂閱關係。

  • map方法,它通過lift方法返回了一個String型別的Observable
//其中T=Integer,R=String。
public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
        return lift(new OperatorMap<T, R>(func));
}
複製程式碼
  • 下面看下OperatorMap這個物件,這個物件實現了operator<R,T>介面,而這個介面繼承於Func1<Subscriber<? super R>, Subscriber<? super T>>,在它實現的call方法中傳入了String型別的Subscriber(目標Subscriber),並返回了Integer型別的Subscriber(代理Subscriber),當它的方法被回撥時,會呼叫目標Subscriber的對應方法,其中在呼叫onNext時,就用上了外部傳入的Func1函式:
    @Override
    public Subscriber<? super T> call(final Subscriber<? super R> o) {
        return new Subscriber<T>(o) {

            @Override
            public void onCompleted() {
                o.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                o.onError(e);
            }

            @Override
            public void onNext(T t) {
                try {
                    o.onNext(transformer.call(t));
                } catch (Throwable e) {
                    Exceptions.throwIfFatal(e);
                    onError(OnErrorThrowable.addValueAsLastCause(e, t));
                }
            }

        };
    }
複製程式碼
  • 接著再回過頭來看lift方法:
    public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
        return new Observable<R>(new OnSubscribe<R>() {
            @Override
            public void call(Subscriber<? super R> o) {
                try {
                    //返回一個Integer型別的Subscriber。
                    Subscriber<? super T> st = hook.onLift(operator).call(o);
                    try {
                        st.onStart();
                        //關鍵方法:Integer型別的OnSubscribe呼叫對應的Subscribe,這個call方法裡面寫了我們的邏輯,當它呼叫onNext(Integer integer)時,實際上呼叫的是onNext(String str)。
                        onSubscribe.call(st);
                    } catch (Throwable e) {
                        if (e instanceof OnErrorNotImplementedException) {
                            throw (OnErrorNotImplementedException) e;
                        }
                        st.onError(e);
                    }
                } catch (Throwable e) {
                    if (e instanceof OnErrorNotImplementedException) {
                        throw (OnErrorNotImplementedException) e;
                    }
                    o.onError(e);
                }
            }
        });
    }
複製程式碼
  • 最後就是呼叫subscribe方法。

相關文章