函數語言程式設計與RxJava(附demo)

mymdeep發表於2017-02-05

函數語言程式設計

在開篇我需要介紹一下什麼叫函數語言程式設計,我先引用網上的一個概念:

函式程式語言最重要的基礎是 λ 演算(lambda calculus)。而且λ演算的函式可以接受函式當作輸入(引數)和輸出(返回值)。

好吧,這樣說,太過於抽象了,我們先舉個簡單的例子,區分一下程式導向,物件導向,和函式式變成。

程式導向

以下是一個求階乘的實現:

   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輸出:

函數語言程式設計與RxJava(附demo)

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);複製程式碼

結果如下:

函數語言程式設計與RxJava(附demo)
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);
                    }
                });複製程式碼

輸出:

函數語言程式設計與RxJava(附demo)

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);
                    }
                });複製程式碼

列印截圖:

函數語言程式設計與RxJava(附demo)

我們可以這樣理解, 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);複製程式碼

輸出結果:

函數語言程式設計與RxJava(附demo)

好了基本就講這麼多,如果感興趣的使用者可以下載我的demo,自己修改些內容測試一下,理解可能會更深刻。
我的demo
更多的開發知識,可以關注我的公眾號:

函數語言程式設計與RxJava(附demo)
Paste_Image.png

相關文章