什麼是 RxJava?
a library for composing asynchronous and event-based programs by using observable sequences.
從 RxJava Github 的介紹,可以很清楚地知道 RxJava 的用途是什麼。
一個利用 Observable Sequences 來達成「非同步」與「事件導向」設計的套件。
先來簡單描述一下 Observable Sequences、非同步、事件導向,這三個專有名詞。
Observable Sequence
首先要先了解什麼是 Observable Pattern(觀察者模式) ,就像我們平常燒開水一樣,打水壺放上瓦斯爐後,我們就會先去做其它事情,等水滾的時候水壺在叫了,才會再回去火關掉,拿煮好的水去使用。
以下的程式是表達人與水壺的關係,重點在於人並沒有一直去檢查水滾了沒有,而是水滾的時候,水壺會自動跟人產生互動。
這就是 Observable Pattern(觀察者模式)。
class Jug {
private Human mHuman;
// 加入要通知的人
public void add(Human human) {
this.mHuman = human;
}
// 水滾了
public void fired() {
this.mHuman.listened();
}
}
class Human {
// 有沒有聽到水滾了
public void listened() {
System.out.print("可以洗澡了!!!");
}
}
Jug jug = new Jub();
Human human = new Human();
jug.add(human);
jug.fired(); // 這時 Human 就知道可以洗澡了!
而 Observable Sequence 的意思就是把我們在寫的程式流程轉化成 Observable Pattern。
非同步
非同步其實是 Observable Pattern 的一個特性,是表示人不用一直在停留在「等水滾,然後去洗澡」的狀態,他可以先去做「別的事情」,等「水滾了」就「可以去洗澡」。
非同步的意思就是「人」跟「水壺」的狀態是處於沒有相關性的情況,可以各自去做自己的事情。
事件導向
事件導向也是 Observable Pattern 的一個特性,因為「人」跟「水壺」的狀態是處於沒有相關性的情況,那到底什麼時候「人」跟「水壺」又開始產生影響了,就是「事件」發生的時候,「水滾了」之後,「人就會聽到」,然後「人就可以去洗澡了」。
為什麼要用 RxJava?
所以 RxJava 就是把你的程式流程做了一些改變,改變成「非同步」與「事件導向」。
那做了這些改變的好處是什麼呢?
當你需要做一些要花一些時間的事情時,不會讓使用者的操作卡在 Loading 的畫面,可以讓使用者先進行其它的操作,當事情作完的時候,再通知使用者讓他能夠進行原本的操作。
其實在使用 RxJava 之前,大家應該都有做過類似的事情,只是使用的方法不一樣。
像是「建立 Thread 來處理事情」,「使用 AsyncTask」,「Handler的postDelayed」,在理論上都是同樣的東西。
那什麼還要使用 RxJava 呢?
懶啊!!!!!
懶得去建立 Thread , 懶得去 new AsyncTask , 還要去 try-catch 抓 exception , 一下要轉到 uiThread , 一下又要轉到 workThread , 煩不煩啊!!!!
更不要說還有不同 AsyncTask 的 doBackground 都跑在同一個 Thread 上,會彼此卡住的同題要處理。
RxJava提供更簡單的方式(?,其實這個看個人),來幫你把這些雜七雜八的事情處理掉。讓你可以更專心地寫你要的功能。
RxJava 怎麼用?
重點就是這個了,好用才要用,不好用就乖乖回去用 Handler / AsyncTask …
在 Github 中 RxJava 提供了很多 method 可以用,但是一般使用上,大部份都是以切換 Thread 跟 轉換資料型態為主,所以會以這個部份做說明。
Hello World!
如果把上面的「水滾了」的程式改成 RxJava 應該會是以下這樣(程式有一點點差異,不過不影響要表達的意思)
Subscriber<String> human = new Subscriber<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String message) {
Log.i(TAG, message);
if (message.contains("水滾了")) {
Log.i(TAG, "可以洗澡了!!!");
}
}
};
Observable<String> jug = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("60度了");
subscriber.onNext("70度了");
subscriber.onNext("80度了");
subscriber.onNext("90度了");
subscriber.onNext("100度水滾了!!!!");
}
});
jug.subscribe(human);
不過以 RxJava 的寫法,通常會建議改成 Chain 的寫法會更簡潔清楚
Observable.just("60度了", "70度了", "80度了", "90度了", "100度水滾了!!!!")
.subscribe(new Action1<String>() {
@Override
public void call(String message) {
Log.i(TAG, message);
if (message.contains("水滾了")) {
Log.i(TAG, "可以洗澡了!!!");
}
}
});
這樣就是「水滾了」的 RxJava 版本了。
切換 Thread
不過剛剛寫好的「水滾了」,其實並沒有「非同步」的特性。
因為「人」與「水壺」都在同一個 Thread 底下工作,所以「水壺」在「滾水」的時候,「人」其實不能去做其它事情。
因此我們需要把「人」與「水壺」放到不同的 Thread 底下工作,才能達到「非同步」的效果。
這也是 RxJava 被人推薦的原因,要做到這件事,只要加入短短 2 行 code 就完成了,如果是使用「AsyncTask」或「Handler」,大概都要重新順一次流程了。
以下是把「水壺」放到 IO Thread,「人」放到 UI Thread 的版本
Observable.just("60度了", "70度了", "80度了", "90度了", "100度水滾了!!!!")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String message) {
Log.i(TAG, message);
if (message.contains("水滾了")) {
Log.i(TAG, "可以洗澡了!!!");
}
}
});
轉換資料型態
有時候就是這樣,程式都寫好了,才發現需求有修改,或是文件有錯,本來以為應該是拿到 String ,結果郤是拿到 Integer ,難道程式又要重寫了嗎?
這也是 RxJava 好用的原因,對於修改原本的程式相對好改許多,把每個動作都仔細的切成一個 method ,再像堆積木一樣,積成一個流程。
Observable.just(60, 70, 80, 90, 100)
.map(new Func1<Integer, String>() {
@Override
public String call(Integer temperature) {
return (temperature == 100) ? "100度水滾了!!!!" : temperature + "度了";
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String message) {
Log.i(TAG, message);
if (message.contains("水滾了")) {
Log.i(TAG, "可以洗澡了!!!");
}
}
});
過濾資料
再次遇到需求調整,希望80度以上再跟「人」通知就好了!
沒問題,還好我有用 RxJava !
Observable.just(60, 70, 80, 90, 100)
.filter(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
return (integer > 80);
}
})
.map(new Func1<Integer, String>() {
@Override
public String call(Integer temperature) {
return (temperature == 100) ? "100度水滾了!!!!" : temperature + "度了";
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String message) {
Log.i(TAG, message);
if (message.contains("水滾了")) {
Log.i(TAG, "可以洗澡了!!!");
}
}
});
加個 filter 就搞定了,是不是真的還好有用 RxJava … XD
結論
RxJava 的功能還很多,感覺 RxJava 無比強大,好像幾乎可以改變原本寫程式的觀念。
這種感覺,在學 Node.js® 的時候也有過一次,就連 PHP 也開始有 ReactPHP 了。
現在 Realtime 的系統越來越常見了,或許接下來會開始越來越接觸到這種 Reactive 的寫法,自己寫程式的邏輯也要再重新適應一次了。
參考網頁
RxJava Github Project Page
给 Android 开发者的 RxJava 详解
RxJava@Open Android
0 意見:
張貼留言