RxJava2.x 分析原始碼,理解操作符FlatMap
需求很簡單
獲取手機儲存卡中的所有檔案.
程式碼如下
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,有問題的話,你來打我啊,哈哈哈,就到這裡了。有問題歡迎指正。
相關文章
- RxJava2.x 從原始碼分析原理RxJava原始碼
- 友好 RxJava2.x 原始碼解析(三)zip 原始碼分析RxJava原始碼
- RxJava2原始碼分析(二):操作符原理分析RxJava原始碼
- RxJava2.X 學習筆記 -- 建立操作符RxJava筆記
- RxJava 原始碼分析系列(四) -操作符變換原理RxJava原始碼
- RxJava2原始碼解讀之 Map、FlatMapRxJava原始碼
- 友好 RxJava2.x 原始碼解析(一)基本訂閱流程RxJava原始碼
- 友好 RxJava2.x 原始碼解析(二)執行緒切換RxJava原始碼執行緒
- ReentrantReadWriteLock原始碼分析及理解原始碼
- JVM 原始碼分析(三):深入理解 CASJVM原始碼
- 深入理解Spring IOC原始碼分析Spring原始碼
- PLSA模型的再理解以及原始碼分析模型原始碼
- Dart語法篇之集合操作符函式與原始碼分析(三)Dart函式原始碼
- Dart 原始碼分析:深入理解 dart:io HttpClientDart原始碼HTTPclient
- JVM 原始碼分析(四):深入理解 park / unparkJVM原始碼
- Retrofit原始碼分析三 原始碼分析原始碼
- Android主流三方庫原始碼分析(五、深入理解RxJava原始碼)Android原始碼RxJava
- Android主流三方庫原始碼分析(四、深入理解GreenDao原始碼)Android原始碼
- Android主流三方庫原始碼分析(一、深入理解OKHttp原始碼)Android原始碼HTTP
- Android主流三方庫原始碼分析(二、深入理解Retrofit原始碼)Android原始碼
- tomcat原始碼分析(第三篇 tomcat請求原理解析--Connector原始碼分析)Tomcat原始碼
- AQS原始碼理解AQS原始碼
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- Android主流三方庫原始碼分析(三、深入理解Glide原始碼)Android原始碼IDE
- tomcat原始碼分析(第四篇 tomcat請求處理原理解析--Container原始碼分析)Tomcat原始碼AI
- 深入理解typeof操作符
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- 【JDK原始碼分析系列】ArrayBlockingQueue原始碼分析JDK原始碼BloC
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼
- 以太坊原始碼分析(43)node原始碼分析原始碼
- 以太坊原始碼分析(52)trie原始碼分析原始碼
- 基於個人理解的springAOP部分原始碼分析,內含較多原始碼,慎入Spring原始碼
- 學習JUC原始碼(3)——Condition等待佇列(原始碼分析結合圖文理解)原始碼佇列