RxJava2.x 分析原始碼,理解操作符FlatMap

weixin_33912246發表於2017-12-12

需求很簡單

獲取手機儲存卡中的所有檔案.

程式碼如下

  File file = new File(Environment.getExternalStorageDirectory().getPath());
  //定義一個被觀察者
  Observable<File> observable = Observable.just(file)
                .flatMap(new Function<File, ObservableSource<File>>() {
                    @Override
                    public ObservableSource<File> apply(@NonNull File file) throws Exception {
                        return listFile(file);
                    }
                });
  //定義一個觀察者
  Observer<File> observer = new Observer<File>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(File file) {
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        };
  //訂閱
  observable.subscribe(observer);
 }
  //遞迴檔案,
  private Observable<File> listFile(File file) {
        if (file.isDirectory()) {
            Observable<File> ob = Observable.fromArray(file.listFiles())
                    .flatMap(new Function<File, ObservableSource<File>>() {
                        @Override
                        public ObservableSource<File> apply(@NonNull File file) throws Exception {
                            return listFile(file);
                        }
                    });
            return ob;
        } else {
            Log.d("cql", file.getName());
            return Observable.just(file);
        }
}

到了這裡,如果你已經理解了這段程式碼的意思,那就沒必要往下看了,但是如果你看了之後感覺不是很懂,或者模稜兩可,那麼可以試著往下看。

這段程式碼其實是之前看視訊教程,裡面老師舉的例子,老師快速的解釋了什麼是FlatMap,然後又快速的寫下了上面的程式碼,幾個下一步,就講解完了。我在旁邊聽的兩臉懵逼,老師水平很高,但是我悟性太差。實在理解不了,沒什麼辦法,只能自己分析。OK,不多BB下面直接開始。

什麼是FlatMap

大家可以簡單的看一下官方的介紹。(雖然我看不太懂)
Flatmap

直接分析

我們先看看在程式碼(被觀察者訂閱觀察者之後)執行下面程式碼後的操作.

 observable.subscribe(observer);

直接看subscribe程式碼

       ObjectHelper.requireNonNull(observer, "observer is null");
        try {
            //和hook有關暫且放一下
            observer = RxJavaPlugins.onSubscribe(this, observer);

            ObjectHelper.requireNonNull(observer, "Plugin returned null Observer");
            //關鍵程式碼是這一句
            subscribeActual(observer);
        } catch (NullPointerException e) { // NOPMD
            throw e;
        } catch (Throwable e) {
            Exceptions.throwIfFatal(e);
            // can't call onError because no way to know if a Disposable has been set or not
            // can't call onSubscribe because the call might have set a Subscription already
            RxJavaPlugins.onError(e);

            NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");
            npe.initCause(e);
            throw npe;
        }

然後定位到這句

subscribeActual(observer);

可以看到,這個方法是個抽象方法

protected abstract void subscribeActual(Observer<? super T> observer);

OK,那麼我們直接找他的實現類。回到前面,我們發現這個被觀察者的實現類實際上是由下面程式碼返回的,那麼很簡單,我們直接確定這個observable的型別就可以了。

Observable<File> observable = Observable.just(file)
       .flatMap(new Function<File, ObservableSource<File>>() {
        @Override
        public ObservableSource<File> apply(@NonNull File file) throws Exception {
           return listFile(file);
     }
 });

經過除錯大法,我們發現了這個實現類為ObservableScalarXMap。先不用管他是什麼,直接進去看subscribeActual

        public void subscribeActual(Observer<? super R> s) {
            ObservableSource<? extends R> other;
            try {
                //關鍵程式碼
                other = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null ObservableSource");
            } catch (Throwable e) {
                EmptyDisposable.error(e, s);
                return;
            }
            if (other instanceof Callable) {
                R u;

                try {
                    u = ((Callable<R>)other).call();
                } catch (Throwable ex) {
                    Exceptions.throwIfFatal(ex);
                    EmptyDisposable.error(ex, s);
                    return;
                }

                if (u == null) {
                    EmptyDisposable.complete(s);
                    return;
                }
                ScalarDisposable<R> sd = new ScalarDisposable<R>(s, u);
                s.onSubscribe(sd);
                sd.run();
            } else {
                other.subscribe(s);
            }
        }

看上面註釋為關鍵程式碼的那一段

ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null ObservableSource");

可以看到呼叫了mapper.apply(value),那麼mapper是神馬呢?我們來看構造方法

       ScalarXMapObservable(T value,
                Function<? super T, ? extends ObservableSource<? extends R>> mapper) {
            this.value = value;
            this.mapper = mapper;
        }

OK,mapper原來就是我們傳進來的function,所以也是直接執行了function的回撥方法apply.
到此為止,我們知道了訂閱之後程式碼執行流程是如何執行到apply裡面去的。
那麼現在,我們繼續分析,先看一下listFile裡面做了啥。

    private Observable<File> listFile(File file) {
        if (file.isDirectory()) {
            Observable<File> ob = Observable.fromArray(file.listFiles())
                    .flatMap(new Function<File, ObservableSource<File>>() {
                        @Override
                        public ObservableSource<File> apply(@NonNull File file) throws Exception {
                            return listFile(file);
                        }
                    });
            return ob;
        } else {
            //為檔案的時候
            Log.d("cql", file.getName());
            return Observable.just(file);
        }
    }

可以看到上面程式碼判斷當file不是個目錄的時候,返回Observable.just(file)。那為什麼要返回這個?
OK 繼續回頭看一下之前提到ObservableScalarXMap的subscribeActual方法(apply的實現的地方),為了不讓大家回去看,我又貼上了一份程式碼,大家繼續向下看就可以了。

 public void subscribeActual(Observer<? super R> s) {
            ObservableSource<? extends R> other;
            try {
                //關鍵程式碼
                other = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null ObservableSource");
            } catch (Throwable e) {
                EmptyDisposable.error(e, s);
                return;
            }
            if (other instanceof Callable) {
                R u;

                try {
                    u = ((Callable<R>)other).call();
                } catch (Throwable ex) {
                    Exceptions.throwIfFatal(ex);
                    EmptyDisposable.error(ex, s);
                    return;
                }

                if (u == null) {
                    EmptyDisposable.complete(s);
                    return;
                }
                ScalarDisposable<R> sd = new ScalarDisposable<R>(s, u);
                s.onSubscribe(sd);
                sd.run();
            } else {
                other.subscribe(s);
            }
        }
    }

關鍵程式碼還是這句

other =ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null ObservableSource");

other是apply執行的返回結果也就是Observable.just(file) 。
然後判斷了Observable.just(file) 是否為多型Callable

          if (other instanceof Callable) {
                R u;

                try {
                    u = ((Callable<R>)other).call();
                } catch (Throwable ex) {
                    Exceptions.throwIfFatal(ex);
                    EmptyDisposable.error(ex, s);
                    return;
                }

                if (u == null) {
                    EmptyDisposable.complete(s);
                    return;
                }
                ScalarDisposable<R> sd = new ScalarDisposable<R>(s, u);
                s.onSubscribe(sd);
                sd.run();
            } else {
                other.subscribe(s);
            }

我們先看看他是不是,繼續除錯大法發現Observable.just(file)返回值ObservableJust,然後看一下原始碼

public final class ObservableJust<T> extends Observable<T> implements ScalarCallable<T>

public interface ScalarCallable<T> extends Callable<T>

OK是Callable的子類,那繼續看,其實後面程式碼已經很明朗了,我們大概掃一下就可以,

   ScalarDisposable<R> sd = new ScalarDisposable<R>(s, u);
                s.onSubscribe(sd);
                sd.run();

然後sd.run方法

            if (get() == START && compareAndSet(START, ON_NEXT)) {
                //關鍵程式碼
                observer.onNext(value);
                if (get() == ON_NEXT) {
                    lazySet(ON_COMPLETE);
                    observer.onComplete();
                }
            }

OK,看到了呼叫了onNext,將最終結果返回給了觀察者。呦西 原來是這樣!如果是檔案的話,就直接用just操作符傳送給觀察者去處理結果。
一種情況我們弄明白了,現在看另一種情況

        if (file.isDirectory()) {
            Observable<File> ob = Observable.fromArray(file.listFiles())
                    .flatMap(new Function<File, ObservableSource<File>>() {
                        @Override
                        public ObservableSource<File> apply(@NonNull File file) throws Exception {
                            return listFile(file);
                        }
                    });
            return ob;
        }

上面程式碼現在看很明朗,但是我當時看非常懵逼。所以簡單分析一下。
和剛才一樣直接分析apply方法的返回值即可。繼續除錯大法,看到返回值為ObservableFlatMap,然後他和Callable並沒有什麼關係,所以看下面程式碼,他應該是走到了else後面other.subscribe(s);

          if (other instanceof Callable) {
                R u;

                try {
                    u = ((Callable<R>)other).call();
                } catch (Throwable ex) {
                    Exceptions.throwIfFatal(ex);
                    EmptyDisposable.error(ex, s);
                    return;
                }

                if (u == null) {
                    EmptyDisposable.complete(s);
                    return;
                }
                ScalarDisposable<R> sd = new ScalarDisposable<R>(s, u);
                s.onSubscribe(sd);
                sd.run();
            } else {
                other.subscribe(s);
            }

OK,繼續分析other.subscribe(s)的方法體

    @Override
    public void subscribeActual(Observer<? super U> t) {

        if (ObservableScalarXMap.tryScalarXMapSubscribe(source, t, mapper)) {
            return;
        }

        source.subscribe(new MergeObserver<T, U>(t, mapper, delayErrors, maxConcurrency, bufferSize));
    }

OK又看到了熟悉的subscribe方法了,到這裡就可以不需要繼續分析下去了,雖然看到了官網文件介紹的MergeObserver。我們看到subscribe方法 就應該知道 下面的流程和之前分析的相似,會繼續呼叫被觀察者的apply方法,形成遞迴。

根據這個例子,簡單總結

當使用變換操作符FlatMap的時候,如果返回的Observable是Callable的子類的時候,直接會將資料發射給觀察者去處理,如果返回的Observable並非Callable的子類,仍然會繼續呼叫apply方法去處理髮射的資料來源,直到他為Callable,然後給觀察者去處理。從這裡可以看出FlatMap操作符很有用!

當然這只是個例子,也只是FlatMap基本的用法,更深入的功能,還要繼續深入理解每一句程式碼的意思。

以上純屬個人觀點,幫助像我一樣悟性差的人去理解rxjava,有問題的話,你來打我啊,哈哈哈,就到這裡了。有問題歡迎指正。

相關文章