在開篇我須要介紹一下什麼叫函數式編程,我先引用網上的一個概念:javascript
函數編程語言最重要的基礎是 λ 演算(lambda calculus)。並且λ演算的函數能夠接受函數看成輸入(參數)和輸出(返回值)。java
好吧,這樣說,太過於抽象了,咱們先舉個簡單的例子,區分一下面向過程,面向對象,和函數式變成。git
如下是一個求階乘的實現:github
public static void main(String[] args) {
int m = 10;
int result = 1;
for(int i = 1;i<=10;i++){
result = result*i;
}
System.out.println(result);
}複製代碼
仍是上面提到過的求階乘的概念:數據庫
public interface Function {
public int call(int x);
}複製代碼
public class MultiplyFunction implements Function {
@Override
public static int call(int x) {
int result = 1;
for(int i = 1;i<=x;i++){
result = result*i;
}
return result;
}
}複製代碼
public static void main(String[] args) {
System.out.println(new MultiplyFunction().call(10));
}複製代碼
public static void main(String[] args) {
System.out.println(call(10));
}
public static int call(int x) {
int result = 1;
while(x>=1){
result =multiply(result,x);
x--;
}
return result;
}
public static int multiply(int x,int y) {
return x*y
}複製代碼
例子有些簡單,不知道是否恰當,容易理解。相對於面向對象的編程,函數式編程無反作用,內部不存在狀態,易於併發。
再通俗點說,函數式編程會將函數也被看成一種數據對象。下面來看一下Android中RXJava的實現。編程
RxJava咱們能夠理解爲是一個觀察者模式的擴展,什麼是觀察者模式?數組
舉個簡單例子,咱們都會給Button設置一個Click事件對吧,對設置 OnClickListener 來講, Button 是被觀察者, OnClickListener 是觀察者,兩者經過 setOnClickListener() 方法產生關係。
OnClickListener一直觀察着Button,當Button被點擊,OnClickListener執行onClick事件。
RxJava 的觀察者模式,與之相似,Observable (可觀察者,即被觀察者)、 Observer (觀察者)、 subscribe (訂閱)、事件。Observable 和 Observer 經過 subscribe() 方法實現訂閱關係,從而 Observable 能夠在須要的時候發出事件來通知 Observer。能夠經過下表進行理解:網絡
點擊事件 | RxJava |
---|---|
Button | Observable |
OnClickListener | Observer |
setOnClickListener() | subscribe() |
onClick() | onNext() onCompleted() onError() |
##簡單實現
下面舉一個基本的例子:併發
Observer<String> observer = new Observer<String>() {
@Override
public void onNext(String s) {
Log.d(tag, "onNext: " + s);
}
@Override
public void onCompleted() {
Log.d(tag, "Completed!");
}
@Override
public void onError(Throwable e) {
Log.d(tag, "Error!");
}
};
Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("aaaa");
subscriber.onNext("bbbb");
subscriber.onNext("cccc");
subscriber.onCompleted();
}
});
observable.subscribe(observer);複製代碼
上面的例子利用just方法還有一種寫法,不用重寫call方法:異步
Observer<String> observer = new Observer<String>() {
@Override
public void onNext(String s) {
Log.d(tag, " just onNext: " + s);
}
@Override
public void onCompleted() {
Log.d(tag, "just Completed!");
}
@Override
public void onError(Throwable e) {
Log.d(tag, "just Error!");
}
};
Observable observable = Observable.just("aaaa","bbbb","cccc");
observable.subscribe(observer);複製代碼
咱們能夠看一下log輸出:
當傳入的是一個數組的時候,可使用from方法
String[] words = {"aaaa", "bbbb", "cccc"};
Observable observable = Observable.from(words);
observable.subscribe(observer);複製代碼
#subscribe()
上面提到的都是去subscribe一個observer,在observer中也是一個一個的任務,若是單獨執行一個任務是否能夠呢?答案是:必須的。
Action1<String> onNextAction = new Action1<String>() {
@Override
public void call(String s) {
Log.d(tag, s);
}
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.d(tag, "error");
}
};
Action0 onCompletedAction = new Action0() {
@Override
public void call() {
Log.d(tag, "completed");
}
};
String[] words = {"aaaa", "bbbb", "cccc"};
Observable observable = Observable.from(words);
observable.subscribe(onNextAction);
observable.subscribe(onNextAction, onErrorAction);
observable.subscribe(onNextAction, onErrorAction, onCompletedAction);複製代碼
結果以下:
在不指定線程的狀況下, RxJava 遵循的是線程不變的原則,即:在哪一個線程調用 subscribe(),就在哪一個線程生產事件;在哪一個線程生產事件,就在哪一個線程消費事件。若是須要切換線程,就須要用到 Scheduler (調度器)。
Schedulers.immediate(): 直接在當前線程運行,至關於不指定線程。這是默認的 Scheduler。
Schedulers.newThread(): 老是啓用新線程,並在新線程執行操做。
Schedulers.io(): I/O 操做(讀寫文件、讀寫數據庫、網絡信息交互等)所使用的 Scheduler。行爲模式和 newThread() 差很少,區別在於 io() 的內部實現是是用一個無數量上限的線程池,能夠重用空閒的線程,所以多數狀況下 io() 比 newThread() 更有效率。不要把計算工做放在 io() 中,能夠避免建立沒必要要的線程。
Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操做限制性能的操做,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小爲 CPU 核數。不要把 I/O 操做放在 computation() 中,不然 I/O 操做的等待時間會浪費 CPU。
另外, Android 還有一個專用的AndroidSchedulers.mainThread(),它指定的操做將在 Android 主線程運行。
對於Android來講異步線程與主線程交互,是一個關鍵點,這裏舉個簡單的例子:
Observer<Bitmap> observer = new Observer<Bitmap>() {
@Override
public void onNext(Bitmap s) {
imageView.setImageBitmap(s);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(ImageActivity.this,"error="+e.getMessage(),Toast.LENGTH_LONG).show();
}
};
Observable observable = Observable.create(new Observable.OnSubscribe<Bitmap>() {
@Override
public void call(Subscriber<? super Bitmap> subscriber) {
Bitmap bitmap = binary2Bitmap(getNetData(imageurl));
subscriber.onNext(bitmap);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io()) // 指定 subscribe() 發生在 IO 線程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回調發生在主線程
;
observable.subscribe(observer);複製代碼
其中 Bitmap bitmap = binary2Bitmap(getNetData(imageurl));是獲取網絡圖片,具體實現方法能夠參照個人demo,可是這個方法因爲有網絡請求,須要放到子線程,因此使用Schedulers.io(),而回調須要設置ImageView,須要放到主線程,因此使用AndroidSchedulers.mainThread()
map是作什麼用的呢,咱們能夠這樣理解,咱們但願傳入的類型是String類型,而處理的類型是int類型,這時應該怎麼辦呢,須要在處理以前,根據必定的邏輯,將string轉成int型。
Observable.just("aaaa","bbb","cc") // 輸入類型 String
.map(new Func1<String, Integer>() {
@Override
public Integer call(String s) { // 參數類型 String
return s.length(); // 返回類型 Bitmap
}
})
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer i) { // 參數類型 Bitmap
Log.e(tag,"length = "+i);
}
});複製代碼
輸出:
關於flatMap的使用場景有點抽象,我先上代碼,而後再介紹:
Observable.just("aaaa","bbb","cc") // 輸入類型 String
.flatMap(new Func1<String, Observable<Integer>>() {
@Override
public Observable<Integer> call(String s) {
Integer[] info = new Integer[3];
info[0] = s.length();
info[1] = s.hashCode();
info[2] = s.getBytes().length;
return Observable.from(info);
}
})
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer i) { // 參數類型 Bitmap
Log.e(tag,"length = "+i);
}
});複製代碼
打印截圖:
咱們能夠這樣理解, flatMap() 中返回的是個 Observable 對象,而且這個 Observable 對象並非被直接發送到了 Subscriber 的回調方法中。
例如上面的例子,能夠看作是,一個String返回了一個Observable,一個Observable執行了三次Action。
顧名思義,filter就是一個過濾,能夠指定過濾條件,例如我指定過濾字符串中含有字符a的字符串:
Observer<String> observer = new Observer<String>() {
@Override
public void onNext(String s) {
Log.d(tag, " filter onNext: " + s);
}
@Override
public void onCompleted() {
Log.d(tag, "filter Completed!");
}
@Override
public void onError(Throwable e) {
Log.d(tag, "filter Error!");
}
};
Observable observable = Observable.just("aaaa","bbbb","cccc")
.filter(new Func1<String, Boolean>() {
@Override
public Boolean call(String s) {
return s.contains("a");
}
});
observable.subscribe(observer);複製代碼
輸出結果:
好了基本就講這麼多,若是感興趣的用戶能夠下載個人demo,本身修改些內容測試一下,理解可能會更深入。
個人demo
更多的開發知識,能夠關注個人公衆號: