來聊事件流與操作符之間的關係
前言
記錄一下這是我的第101篇Medium的技術文章。從iOS的OOP世界,寫到軟體工程的工作紀錄,在職也大概快三年多,從iOS到Flutter,再到Android,OOP世界轉往FRP世界,工作面對過挑戰,曾經加班過夜,曾經要趕專案,曾經製造出無數bug。
職場上也許有很多人真的很強,對我做出來的東西覺得還好,但我覺得最重要的是自己仍在攝取與進步,把開發者的每一天做的實在,就不會困在基礎不扎實的狀態之中。
接下來我們要談的是RxJava中的操作符。
正文
如果說事件流是Rx的本體,那麼操作符就是它的靈魂,我們可以看見每一個操作符在事件流流出事件、值、物件時後面跟著的方法用以對事件流的流出的物件進行各種加工。
以下會對操作符有一些基本的介紹:
以上是RxJava在操作符上比較常見的幾類。那麼,咱們就一個一個慢慢聊。
創建數據流(Observable/Flowable)
創建數據流,就簡單而言,就是把數據直接轉為Observable,這邊可以直接用just()
Observable<String> observable = Observable.just("Hello", "RxJava");
observable.subscribe(System.out::println);
output:
Hello
RxJava
或者可以用fromArray,就直接把陣列轉成數據流:
String[] items = {"Apple", "Banana", "Cherry"};
Observable<String> observable = Observable.fromArray(items);
observable.subscribe(System.out::println);
output:
Apple
Banana
Cherry
或create(),這是一種手動發射數據的方式:
Observable<Integer> observable = Observable.create(emitter -> {
emitter.onNext(1);
emitter.onNext(2);
emitter.onComplete();
});
observable.subscribe(System.out::println);
一個一個用onNext發射。
數據轉換
這些操作符是用於轉換數據流中的數據:
常見的就map()囉:
Observable.just(2)
.map(value -> value * 10)
.subscribe(System.out::println);
output:
20
無序一對多數據轉換
用flatMap():
Observable.just("A", "B")
.flatMap(letter -> Observable.just(letter + "1", letter + "2"))
.subscribe(System.out::println);
output:
A1
A2
B1
B2
有序數據轉換
concatMap()一對多轉換(保證順序)
Observable.just("A", "B")
.concatMap(letter -> Observable.just(letter + "1", letter + "2"))
.subscribe(System.out::println);
output:
A1
A2
B1
B2
最後一個數據流轉換
用switchMap()
過濾數據
用filter()
Observable.just(1, 2, 3, 4, 5)
.filter(value -> value % 2 == 0) // 只保留偶數
.subscribe(System.out::println);
output:
2
4
取前N個數據
用take()
Observable.just(1, 2, 3, 4, 5)
.take(3)
.subscribe(System.out::println);
output:
1
2
3
合併數據流
用merge(),兩個數據流同時發射
Observable<String> observable1 = Observable.just("A", "B", "C");
Observable<String> observable2 = Observable.just("1", "2", "3");
Observable.merge(observable1, observable2)
.subscribe(System.out::println);
output:
A
1
B
2
C
3
合併並對應數據流
用zip()
Observable<Integer> observable1 = Observable.just(1, 2, 3);
Observable<String> observable2 = Observable.just("A", "B", "C");
Observable.zip(observable1, observable2, (num, letter) -> num + letter)
.subscribe(System.out::println);
output:
1A
2B
3C
錯誤處理
RxJava 的異步數據流,錯誤處理非常重要!
使用onErrorReturn() ,發生錯誤時,提供一個預設值:
Observable.just(1, 2, 0, 4)
.map(value -> 10 / value) // 這裡會因為除以 0 出錯
.onErrorReturn(error -> -1) // 這時候返回 -1
.subscribe(System.out::println);
output:
1
2
-1
線程切換
Rx最重要的特性之一:非同步處理,線程操作符下下
Observable.just("Hello")
.subscribeOn(Schedulers.io()) // 在 IO 線程執行
.observeOn(AndroidSchedulers.mainThread()) // 回到主線程
.subscribe(System.out::println);
所謂的IO(Input/Output)線程 指的是用來處理 IO 密集型操作(如網絡請求、文件讀寫、數據庫操作等)的線程。
在Anroid開發中,主線程負責UI操作,如果在主線程進行網絡請求或文件讀取,會導致 ANR(應用無回應)錯誤,因為 UI 會被阻塞。
總的來說Rx的核心概念大概有哪些呢?
✅ 五大核心事件流 (Observable, Flowable, Single, Maybe, Completable)
✅ 背壓 (Backpressure)
✅ 熱/冷數據流 (Hot Observable, Cold Observable)
✅ Subject 和 Processor
✅ 操作符 (map(), flatMap(), switchMap(), filter(), merge() 等)
✅ 線程控制 (Schedulers.io(), Schedulers.computation(), AndroidSchedulers.mainThread())
這邊大致上已經提及了百分之80%了,我想接下來更多的就是實作了!
這邊有可能也會規劃一點Rx的實作篇章,比如創建了數據流之後,還可能會有需要dispose數據流的可能,這時候應該怎麼辦呢?
我想之後再陸續規劃類似的篇章出來!
尾聲
默默的,也寫大約100篇了,還記得從iOS出發時,那時趕著快速的發文,讀完語法,然後進到實作裡,在實作的篇章的時候,大概從不知道OOP到知道OOP,然後接著在iOS中打轉,然後為了找到工作,開始寫Flutter,開始解決雙平台的問題,Well,不得不承認,iOS、Flutter兩者有巨大的差別,但實作起來Flutter確實方便多了。
Android呢?我覺得摸著摸著到現在Android真的就在商業環境不同的機型中打轉,最困境的大概都是底層的不一樣。但實作起來,Ummmm,實作原理都差不了多少。
所以,我都摸的一清二楚了嗎?
Nope,我想我的文章會有這100篇,可能只是剛開始而已。我想我會耕耘在Mobile這個領域一陣子,至少這件事情我是感興趣的。手機市場會持續多久老實說我並不知道,但可以預見的未來是邏輯不滅、框架、語言不滅、思維不變。就算往後Mobile前端覆滅,相應的思維也會由自己跨到其他領域繼續帶著走。
接下來想做點SwiftUI,再回去摸摸iOS,或者找到一些Flutter之前沒有摸完整的框架來實作。做SideProject對我來說還好,但寫寫文字我還算喜歡。