函數式編程與RxJava(附demo)

函數式編程

在開篇我須要介紹一下什麼叫函數式編程,我先引用網上的一個概念: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

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

上面的例子利用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

當傳入的是一個數組的時候,可使用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);複製代碼

結果以下:

Paste_Image.png

線程管理Scheduler

在不指定線程的狀況下, 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

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

關於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

顧名思義,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
更多的開發知識,能夠關注個人公衆號:

Paste_Image.png
相關文章
相關標籤/搜索