RxJava(一)概述與基本使用
- RxJava(一)概述與基本使用
- RxJava(二)創建操作符
RxJava是近年來非常火熱且複雜的Android框架,本文基於RxJava 1.2.9來對其進行分析。
使用RxJava需要在build.gradle
中修改如下代碼:
dependencies {
...
compile 'io.reactivex:rxjava:1.2.9' //需要添加的代碼
compile 'io.reactivex:rxandroid:1.2.1' //rxandroid的依賴,基於rxjava的擴展庫
}
一、RxJava概述
RxJava是函數響應式編程在Java語言上的實現,在瞭解RxJava之前我們先來簡單學習下什麼是函數響應式編程。
函數響應式編程
函數響應式編程是函數式編程和響應式編程這兩種編程範式的結合。
函數式編程(Functional Programming)是通過函數的調用與組合來處理數據,獲取計算結果的一種編程範式。
在函數式編程中,函數是”第一等公民”,即與其他數據類型地位相同:
- 可以賦值給其他變量,因爲在在函數式編程中,只用”表達式”,即每一步都是一個運算過程,都有返回值,函數也必須都有返回值
- 也可以作爲參數傳遞給其他函數,比如閉包作爲參數傳遞。
函數式編程的優點:
- 簡潔易懂,通過函數的鏈式調用使代碼可讀性更強
- 只依賴輸入的特性,每一個函數都可以看做一個獨立的單元,使代碼更易管理
- 由於函數不修改變量,所以不需要考慮死鎖的問題,易於併發編程
響應式編程(Reactive Programming)是一種面向數據流和變化傳播的編程範式。
在響應式編程中,任何的事件都看做是數據流的形式。上游發射數據流,下游監聽數據流,在傳遞的過程中,對數據流進行過濾,轉變,合併,去重等處理,當下遊接受到數據流時,對其做出響應。
在界面顯示中,將要顯示的數據源(從網絡請求,數據庫查詢中得到)以數據流的形式,通過一系列的流轉過程(對數據進行處理,後臺線程發送到UI線程),交給界面,界面在對數據做出相應的響應。在界面的交互中,也是如此,將用戶輸入的點擊,觸摸等事件以數據流的形式經過層層傳遞交給對應的窗口,在到對應的控件,控件監聽到相應的事件後,響應用戶的行爲。
函數響應式編程(Functional reactive programming)是通過函數塊(map,reduce,filter)來處理異步數據流的一種編程範式。
在RxJava中,函數響應式編程通過設置一個可觀察對象(Observable)和觀察者(Observer/Subscriber),Subscriber監聽Observable的事件,Observable以數據流的形式發送事件,再通過一系列函數的鏈式調用(map/flatmap/filter等)對數據流進行轉變和通過線程調度器(Scheduler)對數據流進行併發處理,最後由Subscriber接受到事件後對事件作出響應。
RxJava的定義
瞭解了函數響應式編程後,我們就能理解RxJava的定義了。
RxJava是響應式擴展的JVM實現:一個使用可觀察序列組成異步的,基於事件的程序的庫。
總的來說,RxJava就是一個實現異步操作的庫,相比於AsyncTask/Handler等異步操作的機制來說,RxJava的優點在於其使用了函數式的編程範式,使用函數的鏈式調用對數據流的發送,流轉,接受,響應進行處理,使得代碼異常簡潔明瞭,再加上JDK8中支持的lambda表達式可以使代碼變的更爲簡化。
二、觀察者模式
RxJava的實現使用了觀察者模式,我們就來講一下觀察者模式。
什麼是觀察者模式
觀察者模式的機制是存在一個被觀察者和觀察者,觀察者對被觀察者的某種特徵進行監聽,當這種特徵發生變化時,觀察者立刻做出反應。例如,在監獄裏,獄警是觀察者,犯人是被觀察者,獄警對犯人的打開牢門的行爲進行了監聽,當犯人打開牢門時,獄警需要立刻衝上去把犯人制服。這就是觀察者模式,獄警需要時刻緊盯犯人的行爲,觀察其是否有打開牢門的行爲。
編程中的觀察者模式
程序中的觀察者模式則有略微不同,觀察者不需要時刻去緊盯被觀察者,而是去訂閱(Subscribe)或註冊(Register)觀察者感興趣的行爲,當被觀察者發生這種行爲時,讓被觀察者去通知他(在上例中就是獄警告訴犯人,你要開牢門的時候要提醒我)。
通常的模式是這樣的:Observable
內部有一個成員變量mObserver
,通過訂閱函數setObserver
來設置訂閱者,建立訂閱關係,同時在被觀察的事件發生時調用通知函數notifyObserver
來通知mObserver
去調用它的響應函數。
點擊事件中的觀察者模式
Android中有很多觀察者的例子,比如註冊監聽事件就是一個很典型的例子。被觀察者View
調用setOnClickListener
來給自己設置一個觀察者OnClickListener
,約定好OnClickListener
訂閱了View
的點擊事件。當View
被點擊時,會調用performClick
方法去通知OnClickListener
,調用OnClickListener.onClick
方法,即OnClickListener
對點擊事件作出響應。
三、RxJava的基本使用
RxJava中基本的實現爲創建觀察者,創建被觀察者,建立訂閱關係
第一步:創建Subscriber/Observer
首先是創建一個觀察者Subscriber/Observer
,其中Observer
是一個接口,其中包含onNext(T t)
,onCompleted()
,onError(Throwable e)
,三個抽象方法,而Subscriber
是一個抽象類,實現了Observer
接口,上述三個方法的實現由其子類來實現,在使用上兩者區別不大,在Observable.subscribe
方法中如果傳入Observer
,會將其封裝成Subscriber
來進行訂閱的。Subscriber
提供了更多的功能,在之後再進行講解。
創建Subscriber
:
Subscriber<String> subscriber = new Subscriber<>() {
@Override
public void onNext(String s) {
Log.d("TAG", "onNext: " + s);
}
@Override
public void onCompleted() {
Log.d("TAG", "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.d("TAG", "onError");
}
};
RxJava中將事件看做是一個隊列,關於這三個事件回調方法作用如下:
onNext(T t)
:不斷的處理下一個事件,直至隊尾onCompleted()
:當不在有新的事件時,調用這個方法作爲結束標誌,這個方法會在最後一個onNext
調用之後調用onError(Throwable e)
:當事件隊列出現異常時,會觸發這個方法,並終止事件隊列,不在處理新的事件。
onError
和onCompleted
在一個處理流裏面只會調用一個。
第二步:創建Observable
調用Observable.create(OnSubcribe)
來創建一個Observable
,OnSubscirbe
繼承了Action1<T>
接口,Action1<T>
接口中只包含一個方法void call(T t)
。在RxJava源碼中有很多ActionX
的接口,X表示接口的泛型個數和call
方法的參數個數,call
中每個參數與泛型類型一一對應。
我們先來看OnSubscribe
的代碼:
/**
* Invoked when Observable.subscribe is called.
* @param <T> the output value type
*/
public interface OnSubscribe<T> extends Action1<Subscriber<? super T>> {
// cover for generics insanity
}
從註釋可以看出在建立訂閱關係Observable.subscribe
調用後會去調用OnSubscribe.call
方法,所以call
中就應該實現Subscriber
對事件作出的響應的處理邏輯。
創建Observable:
Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscribe<? super String> subscriber) {
//subcricber對事件的響應邏輯
subscriber.onNext("first");
subscriber.onNext("second");
subscriber.onNext("third");
subscriber.onCompleted();
}
} );
除了通過Observable.create
來創建Observable
還可以通過just(T... t)
,from(T[] t)
,from(Iterable<? extends T> iterable)來進行創建
Observable`
just
傳入的是多個事件參數:
Observable observable = Observable.just("first", "second", "third");
from
傳入的是一個事件數組:
String[] s = {"first", "second", "third"};
Observable observable = Observable.from(s);
上述兩種方式都和第一種方式沒有區別,just
和from
都在其內部對事件回調函數進行了調用
第三步:建立訂閱關係
通過Observable.subscribe(subscriber)
來進行訂閱關係的建立
observable.subscribe(subscirber);
subscribe
返回的不是Observable
,是Subscription
接口,後序系列詳解這個接口
除了這種方式,RxJava還提供了訂閱不完整定義的事件回調函數,在subscribe
方法中直接傳入事件回調函數:
Subscription subscribe(final Action1<? super T> onNext)
Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError)
Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError, final Action0 onCompleted) {
前面已經講過了ActionX
接口的含義,這裏subscribe
的三個重載就是可以不用麻煩的創建Subscriber
,而是直接去實現回調方法就行了。三個回調方法與重載參數的順序是確定的,看上述方法參數的形參名就知道了。
以第三個方法爲例,實現跟之前創建Subscriber
一樣的效果:
observable.subscribe(new Action1<String>() {
@Override
public void call(String s) {
System.out.println("onNext: " + s);
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
System.out.println("onError");
}
}, new Action0() {
@Override
public void call() {
System.out.println("onCompleted");
}
});
可以根據方法的重載(第一種和第二種情況),不用將三個方法都實現,這樣未實現的方法就默認爲什麼操作都不執行。
通過以上方式,我們就實現了對RxJava的基本使用,RxJava使用函數式編程的思想,支持鏈式調用,合起來代碼如下:
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
//subcricber對事件的響應邏輯
subscriber.onNext("first");
subscriber.onNext("second");
subscriber.onNext("third");
subscriber.onCompleted();
}
}).subscribe(new Subscriber<String>() {
@Override
public void onNext(String s) {
System.out.println("onNext: " + s);
}
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.out.println("onError");
}
});
運行結果如下:
onNext: first
onNext: second
onNext: third
onCompleted
整個的調用流程如下:
首先通過Observable.create(OnSubscribe)
來創建一個Observable
對象,(當然你也可以通過just
或者from
的方式來創建,這樣就不需要OnSubscribe
了,在內部會自動去調用Subscriber
的事件回調方法)。然後調用Observable.subcribe(Subscriber)
傳入一個實現了Subscriber
的實現類,這個實現類裏面實現了onNext
,onCompleted
,onError
三個方法。
在Observable.subcribe(Subscriber)
的內部會去調用OnSubscribe.call()
這個方法,而我們在創建Observable
時傳入的OnSubscribe
的匿名內部類中實現了call(Subscriber<? super String> subscriber)
方法,去調用傳入進來的subscriber
的事件回調的三個函數,來對事件作出響應。
由此可以發現,RxJava中的觀察者模式與一般的不同點在於:
- RxJava中的事件是一個虛擬的事件,不是來源於外部注入的事件,而是是在創建被觀察者的時候就已經將事件給注入進去了。
- RxJava中被觀察者是在與觀察者建立訂閱關係的同時,內部調用
OnSubscribe
的call
方法去通知觀察者對事件作出響應的。
四、RxJava的線程調度
前面已經講述了RxJava的基本使用,但是你看了之後可能發現這個RxJava不是多此一舉,整些花裏胡哨的,直接調用幾個方法去對這些事件進行處理不就行了,爲什麼還要整那些觀察者,被觀察者幹嘛呢。剛開始我學習RxJava的時候也是這樣覺得的,就覺得這個東西沒什麼用,還把自己搞的暈頭轉向的,但是後來學習了RxJava的異步處理之後才知道這樣做的好處。其實前面也有將到RxJava的核心思想和優勢,就是在於對數據流的轉換和對異步操作的處理。所以這裏我們就來先講一下RxJava中的線程調度。
Scheduler
RxJava中提供了一個Scheduler
——線程調度器的機制,其作用是指定RxJava中的每個過程在什麼線程中運行,所以現在你可能就明白了一個很簡單的事件響應爲什麼要分成多個過程來處理了,其原因就是爲了利於線程的調度。
在Schedulers
中定義了幾個常用的Scheduler
(幾個靜態方法的返回值):
Schedulers.immediate()
:默認的Scheduler
,直接在當前線程運行,相當於不指定線程Schedulers.newThread()
:啓動一個新線程,在新線程中執行任務Scheduler.io()
:用於IO異步阻塞操作的Scheduler
,內部是用無上限的線程池實現的,可以重用空閒的線程,效率比newThread
要高。不要把計算操作放在這個Scheduler
中。Scheduler.computation()
:用於事件循環,回調以及其他計算工作的Scheduler
,內部是一個固定大小爲CPU核數的線程池,不能把IO阻塞操作放在這個線程中Schedulers.from(executor)
:使用指定的executor
作爲Scheduler
AndroidSchedulers.mainThread()
:RxAndroid中添加的Scheduler
,表示在Android主線程中運行(RxAndroid是一個基於RxJava的擴展庫,兼容了Android的一些特性)
現在我們就可以去設置每個過程中的Scheduler
來指定每個過程的執行線程了。在RxJava中通過subcsribeOn()
和observeOn()
來設置執行線程,subscribeOn
指定Observable.OnSubscribe
調用call()
方法時所在的線程,即事件產生的線程,observeOn()
指定Subscriber
事件回調的執行線程,即事件響應的線程。
那麼現在我們就來寫一個從IO線程讀取圖片的例子:
Observable.create(new Observable.OnSubscribe<Bitmap>() {
@Override
public void call(Subscriber<? super Bitmap> subscriber) {
Bitmap bitmap = BitmapFactory.decodeFile(picPath);
subscriber.onNext(bitmap);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Bitmap>() {
@Override
public void onCompleted() {
Toast.makeText(RxJavaActivity.this, "圖片加載成功", Toast.LENGTH_LONG).show();
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Bitmap bitmap) {
mIvLocalImage.setImageBitmap(bitmap);
}
});
上述代碼就實現了在IO
線程中去讀取SD卡上的圖片,然後在主線程中顯示出來。將上述代碼中完整的subscriber
實現變爲不完整的事件回調函數,並且使用lambda表達式,代碼如下:
Observable.<Bitmap>create(subscriber -> {
Bitmap bitmap = BitmapFactory.decodeFile(picPath);
subscriber.onNext(bitmap);
subscriber.onCompleted();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
bitmap -> mIvLocalImage.setImageBitmap(bitmap),
e -> {},
() -> Toast.makeText(RxJavaActivity.this, "圖片加載成功", Toast.LENGTH_LONG).show()
);
RxJava配合Lambda表達式實現異步操作,是不是使用起來特別方便簡潔!
ps:以上就是關於RxJava的概述以及RxJava的基本使用了,關於RxJava的後續學習我會在後面的文章中進行講解的,敬請關注!