關于RxJava最友好的文章
RxJava到底是什么?讓我們直接跳過官方那種晦澀的追求精確的定義,其實初學RxJava只要把握兩點: 觀察者模式 和 異步 ,就基本可以熟練使用RxJava了。
異步在這里并不需要做太多的解釋,因為在概念和使用上,并沒有太多高深的東西。大概就是你腦子里想能到的那些多線程,線程切換這些東西。我會在后面會講解它的用法。
我們先把 觀察者模式 說清楚
“按下開關,臺燈燈亮”
在這個事件中, 臺燈作為觀察者,開關作為被觀察者,臺燈透過電線來觀察開關的狀態來并做出相應的處理
觀察上圖,其實已經很明了了,不過需要指出一下幾點( 對于下面理解RxJava很重要 ):
- 開關(被觀察者)作為事件的 產生方 (生產“開”和“關”這兩個事件),是 主動 的,是整個開燈事理流程的 起點 。
- 臺燈(觀察者)作為事件的 處理方 (處理“燈亮”和“燈滅”這兩個事件),是 被動 的,是整個開燈事件流程的 終點 。
- 在起點和終點之間,即事件傳遞的過程中是可以被 加工,過濾,轉換,合并等等方式 處理的(上圖沒有體現,后面對會講到)。
我必須苦口婆心的告訴你:我們總結的這三點對于我們理解RxJava非常重要。因為上述三條分別對應了RxJava中被觀察者(Observable),觀察者(Observer)和操作符的職能。而 觀察者模式又是RxJava程序運行的骨架 。
好了,我假設你已經完全理解了我上面講述的東西。我們正式進入RxJava!
RxJava也是基于觀察者模式來組建自己的程序邏輯的,就是構建 被觀察者(Observable),觀察者(Observer/Subscriber) ,然后建立二者的訂閱關系(就像那根電線,連接起臺燈和開關)實現 觀察 ,在事件傳遞過程中還可以 對事件做各種處理 。
Tips: Observer是觀察者的接口, Subscriber是實現這個接口的抽象類,因此兩個類都可以被當做觀察者,由于Subscriber在Observe的基礎上做了一些拓展,加入了新的方法,一般會更加傾向于使用Subscriber。
創建被觀察者
-
正常模式:
Observable switcher=Observable.create(new Observable.OnSubscribe<String>(){
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("On");
subscriber.onNext("Off");
subscriber.onNext("On");
subscriber.onNext("On");
subscriber.onCompleted();
}
});
這是最正宗的寫法,創建了一個開關類,產生了五個事件,分別是:開,關,開,開,結束。
-
偷懶模式1
Observable switcher=Observable.just("On","Off","On","On");
-
偷懶模式2
String [] kk={"On","Off","On","On"};
Observable switcher=Observable.from(kk);
偷懶模式是一種簡便的寫法,實際上也都是 被觀察者 把那些信息"On","Off","On","On",包裝成onNext("On")這樣的事件依次發給 觀察者 ,當然,它自己補上了onComplete()事件。
以上是最常用到的創建方式,好了,我們就創建了一個開關類。
創建觀察者
-
正常模式
Subscriber light=new Subscriber<String>() {
@Override
public void onCompleted() {
//被觀察者的onCompleted()事件會走到這里;
Log.d("DDDDDD","結束觀察...\n");
}
@Override
public void onError(Throwable e) {
//出現錯誤會調用這個方法
}
@Override
public void onNext(String s) {
//處理傳過來的onNext事件
Log.d("DDDDD","handle this---"+s)
}
這也是比較常見的寫法,創建了一個臺燈類。
-
偷懶模式(非正式寫法)
Action1 light=new Action1<String>() {
@Override
public void call(String s) {
Log.d("DDDDD","handle this---"+s)
}
}
之所以說它是非正式寫法,是因為Action1是一個單純的人畜無害的接口,和Observer沒有啥關系,只不過它可以當做觀察者來使,專門處理onNext 事件,這是一種為了簡便偷懶的寫法。當然還有Action0,Action2,Action3...,0,1,2,3分別表示call()這個方法能接受幾個參數。如果你還不懂,可以暫時跳過。后面我也會盡量使用new Subscriber方式,創建正統的觀察者,便于你們理解。
訂閱
現在已經創建了觀察者和被觀察者,但是兩者還沒有聯系起來
switcher.subscribe(light);
我猜你看到這里應該有疑問了,為什么是開關訂閱了臺燈?應該是臺燈訂閱了開關才對啊。臥槽,到底誰觀察誰啊!!
大家冷靜,把刀放下,有話慢慢說,
是這樣的,臺燈觀察開關,邏輯是沒錯的,而且正常來看就應該是light.subscribe(switcher)才對,之所以“開關訂閱臺燈”,是為了保證 流式API調用風格
啥是流式API調用風格?
//這就是RxJava的流式API調用
Observable.just("On","Off","On","On")
//在傳遞過程中對事件進行過濾操作
.filter(new Func1<String, Boolean>() {
@Override
public Boolean call(String s) {
return s!=null;
}
})
.subscribe(mSubscriber);
上面就是一個非常簡易的RxJava流式API的調用:同一個調用主體一路調用下來,一氣呵成。
由于被觀察者產生事件,是事件的起點,那么開頭就是用Observable這個主體調用來創建被觀察者,產生事件,為了保證流式API調用規則,就直接讓Observable作為唯一的調用主體,一路調用下去。
一句話,背后的真實的邏輯依然是臺燈訂閱了開關,但是在表面上,我們讓開關“假裝”訂閱了臺燈,以便于保持流式API調用風格不變。
好了,現在分解動作都完成了,已經架構了一個基本的RxJava事件處理流程。
我們再來按照觀察者模式的運作流程和流式Api的寫法復習一遍:
流程圖如下:
結合流程圖的相應代碼實例如下:
//創建被觀察者,是事件傳遞的起點
Observable.just("On","Off","On","On")
//這就是在傳遞過程中對事件進行過濾操作
.filter(new Func1<String, Boolean>() {
@Override
public Boolean call(String s) {
return s!=null;
}
})
//實現訂閱
.subscribe(
//創建觀察者,作為事件傳遞的終點處理事件
new Subscriber<String>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","結束觀察...\n");
}
@Override
public void onError(Throwable e) {
//出現錯誤會調用這個方法
}
@Override
public void onNext(String s) {
//處理事件
Log.d("DDDDD","handle this---"+s)
}
);
嗯,基本上我們就把RxJava的骨架就講完了,總結一下:
-
創建被觀察者,產生事件
-
設置事件傳遞過程中的過濾,合并,變換等加工操作。
-
訂閱一個觀察者對象,實現事件最終的處理。
Tips: 當調用訂閱操作(即調用Observer.subscribe()方法)的時候,被觀察者才真正開始發出事件。
現在開始講異步操作?別著急,事件的產生起點和處理的終點我們都比較詳細的講解了,接下來我們好好講講事件傳遞過程中發生的那些事兒...
RxJava的操作符
即使你已經看了我上面那段講解,Rxjava可能還打動不了你,沒關系,事件產生的起點和消費的終點其實沒那么吸引人,真正有意思的是事件傳遞過程中的那些鬼斧神工的操作。
由于篇幅的限制,我只講兩三個操作,其他的操作請看我的 RxJava操作的Demo ( 記得在github給我點star或者follow一下,不然我就坐在地上不起來,哼 )
變換
Map操作
比如被觀察者產生的事件中只有圖片文件路徑;,但是在觀察者這里只想要bitmap,那么就需要 類型變換 。
Observable.create(new Observable.just(getFilePath())
//使用map操作來完成類型轉換
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String s) {
//顯然自定義的createBitmapFromPath(s)方法,是一個極其耗時的操作
return createBitmapFromPath(s);
}
})
.subscribe(
//創建觀察者,作為事件傳遞的終點處理事件
new Subscriber<Bitmap>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","結束觀察...\n");
}
@Override
public void onError(Throwable e) {
//出現錯誤會調用這個方法
}
@Override
public void onNext(Bitmap s) {
//處理事件
showBitmap(s)
}
);
- 實際上在使用map操作時,new Func1<String, Bitmap>() 就對應了類型的轉你方向,String是原類型,Bitmap是轉換后的類型。在call()方法中,輸入的是原類型,返回轉換后的類型
你認真看完上面的代碼就會覺得,何必在過程中變換類型呢?我直接在事件傳遞的終點,在觀察者中變換就行咯。老實說,你這個想法沒毛病,但實際上,上面寫的代碼是不合理的。
我在代碼中也提到,讀取文件,創建bitmap可能是一個耗時操作,那么就應該在子線程中執行,主線程應該僅僅做展示。那么線程切換一般就會是比較復雜的事情了。但是在Rxjava中,是非常方便的。
Observable.create(new Observable.just(getFilePath())
//指定了被觀察者執行的線程環境
.subscribeOn(Schedulers.newThread())
//將接下來執行的線程環境指定為io線程
.observeOn(Schedulers.io())
//使用map操作來完成類型轉換
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String s) {
//顯然自定義的createBitmapFromPath(s)方法,是一個極其耗時的操作
return createBitmapFromPath(s);
}
})
//將后面執行的線程環境切換為主線程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
//創建觀察者,作為事件傳遞的終點處理事件
new Subscriber<Bitmap>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","結束觀察...\n");
}
@Override
public void onError(Throwable e) {
//出現錯誤會調用這個方法
}
@Override
public void onNext(Bitmap s) {
//處理事件
showBitmap(s)
}
);
由上面的代碼可以看到,使用操作符將事件處理逐步分解,通過線程調度為每一步設置不同的線程環境,完全解決了你線程切換的煩惱。可以說線程調度+操作符,才真正展現了RxJava無與倫比的魅力。
flatmap操作
先提出一個需求,查找一個學校每個班級的每個學生,并打印出來。
如果用老辦法:先讀出所有班級的數據,循環每個班級。再循環中再讀取每個班級中每個學生,然后循環打印出來。
還是得說,這種想法,沒毛病,就是嵌套得有點多。
Rxjava說:我不是針對誰...
//創建被觀察者,獲取所有班級
Observable.from(getSchoolClasses())
.flatMap(new Func1<SingleClass, Observable<Student>>() {
@Override
public Observable<Student> call(SingleClass singleClass) {
//將每個班級的所有學生作為一列表包裝成一列Observable<Student>,將學生一個一個傳遞出去
return Observable.from(singleClass.getStudents());
}
})
.subscribe(
//創建觀察者,作為事件傳遞的終點處理事件
new Subscriber<Student>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","結束觀察...\n");
}
@Override
public void onError(Throwable e) {
//出現錯誤會調用這個方法
}
@Override
public void onNext(Student student) {
//接受到每個學生類
Log.d("DDDDDD",student.getName())
}
);
好了,基本上按照RxJava的骨架搭起來就能完成需求。你說棒不棒??
其實FlatMap是比較難懂的一個操作符,作為初學者其實會用就好,所以我推薦的對于FlatMap的解釋是:將每個Observable產生的事件里的信息再包裝成新的Observable傳遞出來,
那么為什么FlatMap可以破除嵌套難題呢?
就是因為FlatMap可以再次包裝新的Observable,而每個Observable都可以使用from(T[])方法來創建自己,這個方法接受一個列表,然后將列表中的數據包裝成一系列事件。
異步(線程調度)
異步是相對于主線程來講的子線程操作,在這里我們不妨使用線程調度這個概念更加貼切。
首先介紹一下RxJava的線程環境有哪些選項:
在講解Map操作符時,已經提到了線程調度,在這里我用更加簡介的代碼代替:
//new Observable.just()執行在新線程
Observable.create(new Observable.just(getFilePath())
//指定在新線程中創建被觀察者
.subscribeOn(Schedulers.newThread())
//將接下來執行的線程環境指定為io線程
.observeOn(Schedulers.io())
//map就處在io線程
.map(mMapOperater)
//將后面執行的線程環境切換為主線程,
//但是這一句依然執行在io線程
.observeOn(AndroidSchedulers.mainThread())
//指定線程無效,但這句代碼本身執行在主線程
.subscribeOn(Schedulers.io())
//執行在主線程
.subscribe(mSubscriber);
實際上線程調度只有subscribeOn()和observeOn()兩個方法。對于初學者,只需要掌握兩點:
-
subscribeOn()它指示Observable在一個指定的調度器上 創建 (只作用于被觀察者創建階段)。只能指定一次,如果指定多次則以第一次為準
-
observeOn()指定在事件傳遞(加工變換)和最終被處理(觀察者)的發生在哪一個調度器。可指定多次,每次指定完都在下一步生效。
線程調度掌握到這個程度,在入門階段時絕對夠用的了。
結尾
好了,對于RxJava整個入門文章到這里就完全結束了,現在再來回看RxJava,你會發現, 它就是在觀察者模式的骨架下,通過豐富的操作符和便捷的異步操作來完成對于復雜業務的處理 。
我相信你對于整個RxJava的骨架,以及執行流程應該有了相當的了解,現在就只需要多練習一下操作符的用法了。
本文沒有介紹太多的操作符,很多沒來得及介紹的操作符的用法實例都放在github上的 RxJavaDemo 項目上了,后期還會繼續加上更多操作符的使用,歡迎大家上去看看,對照代碼,手機運行一下。
大家多給點star!!順便follow一下,接下來,我也會慢慢整理出一些別的有用的項目分享給大家。
來自:http://www.jianshu.com/p/6fd8640046f1