RxJava 好在哪
換句話說,『同樣是做異步,為什么人們用它,而不用現成的 AsyncTask / Handler / XXX / ... ?』
一個詞:**簡潔**。
異步操作很關鍵的一點是程序的簡潔性,因為在調度過程比較復雜的情況下,異步代碼經常會既難寫也難被讀懂。 Android 創造的 AsyncTask 和Handler ,其實都是為了讓異步代碼更加簡潔。RxJava 的優勢也是簡潔,但它的簡潔的與眾不同之處在于**,隨著程序邏輯變得越來越復雜,它依然能夠保持簡潔。**
假設有這樣一個需求:界面上有一個自定義的視圖 *imageCollectorView* ,它的作用是顯示多張圖片,并能使用 *addImage(Bitmap)* 方法來任意增加顯示的圖片。現在需要程序將一個給出的目錄數組 *File[] folders* 中每個目錄下的 png 圖片都加載出來并顯示在*imageCollectorView* 中。需要注意的是,由于讀取圖片的這一過程較為耗時,需要放在后臺執行,而圖片的顯示則必須在 UI 線程執行。常用的實現方式有多種,我這里貼出其中一種:
~~~
new Thread() {
@Override
public void run() {
super.run();
for (File folder : folders) {
File[] files = folder.listFiles();
for (File file : files) {
if (file.getName().endsWith(".png")) {
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}
}.start();
~~~
而如果使用 RxJava ,實現方式是這樣的:
~~~
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
});
~~~
那位說話了:『你這代碼明明變多了啊!簡潔個毛啊!』大兄弟你消消氣,我說的是邏輯的簡潔,不是單純的代碼量少(邏輯簡潔才是提升讀寫代碼速度的必殺技對不?)。觀察一下你會發現, RxJava 的這個實現,是一條從上到下的鏈式調用,沒有任何嵌套,這在邏輯的簡潔性上是具有優勢的。當需求變得復雜時,這種優勢將更加明顯(試想如果還要求只選取前 10 張圖片,常規方式要怎么辦?如果有更多這樣那樣的要求呢?再試想,在這一大堆需求實現完兩個月之后需要改功能,當你翻回這里看到自己當初寫下的那一片迷之縮進,你能保證自己將迅速看懂,而不是對著代碼重新捋一遍思路?)。
另外,如果你的 IDE 是 Android Studio ,其實每次打開某個 Java 文件的時候,你會看到被自動 Lambda 化的預覽,這將讓你更加清晰地看到程序邏輯:
~~~
Observable.from(folders)
.flatMap((Func1) (folder) -> { Observable.from(file.listFiles()) })
.filter((Func1) (file) -> { file.getName().endsWith(".png") })
.map((Func1) (file) -> { getBitmapFromFile(file) })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((Action1) (bitmap) -> { imageCollectorView.addImage(bitmap) });
~~~
* * * * *
**如果你習慣使用 Retrolambda ,你也可以直接把代碼寫成上面這種簡潔的形式。而如果你看到這里還不知道什么是 Retrolambda ,我不建議你現在就去學習它。原因有兩點:1. Lambda 是把雙刃劍,它讓你的代碼簡潔的同時,降低了代碼的可讀性,因此同時學習 RxJava 和 Retrolambda 可能會讓你忽略 RxJava 的一些技術細節;2. Retrolambda 是 Java 6/7 對 Lambda 表達式的非官方兼容方案,它的向后兼容性和穩定性是無法保障的,因此對于企業項目,使用 Retrolambda 是有風險的。所以,與很多 RxJava 的推廣者不同,我并不推薦在學習 RxJava 的同時一起學習 Retrolambda。事實上,我個人雖然很欣賞 Retrolambda,但我從來不用它。**
* * * * *
在Flipboard 的 Android 代碼中,有一段邏輯非常復雜,包含了多次內存操作、本地文件操作和網絡操作,對象分分合合,線程間相互配合相互等待,一會兒排成人字,一會兒排成一字。如果使用常規的方法來實現,肯定是要寫得欲仙欲死,然而在使用 RxJava 的情況下,依然只是一條鏈式調用就完成了。它很長,但很清晰。
所以, RxJava 好在哪?就好在簡潔,好在那把什么復雜邏輯都能穿成一條線的簡潔。