函數(shù)式編程與RxJava(附demo)

函數(shù)式編程

在開篇我需要介紹一下什么叫函數(shù)式編程轨香,我先引用網(wǎng)上的一個(gè)概念:

函數(shù)編程語言最重要的基礎(chǔ)是 λ 演算(lambda calculus)朝聋。而且λ演算的函數(shù)可以接受函數(shù)當(dāng)作輸入(參數(shù))和輸出(返回值)弱贼。

好吧,這樣說扬跋,太過于抽象了桩砰,我們先舉個(gè)簡單的例子拓春,區(qū)分一下面向過程释簿,面向?qū)ο笱怯纾秃瘮?shù)式變成。

面向過程

以下是一個(gè)求階乘的實(shí)現(xiàn):

   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);
    }

面向?qū)ο?/h2>

還是上面提到過的求階乘的概念:

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));
    }

函數(shù)式編程

    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
}

總結(jié)

例子有些簡單煮纵,不知道是否恰當(dāng)懂鸵,容易理解行疏。相對于面向?qū)ο蟮木幊蹋瘮?shù)式編程無副作用,內(nèi)部不存在狀態(tài),易于并發(fā)。
再通俗點(diǎn)說,函數(shù)式編程會將函數(shù)也被當(dāng)作一種數(shù)據(jù)對象。下面來看一下Android中RXJava的實(shí)現(xiàn)爹土。

RXJava

RxJava我們可以理解為是一個(gè)觀察者模式的擴(kuò)展挟阻,什么是觀察者模式?

觀察者模式

舉個(gè)簡單例子,我們都會給Button設(shè)置一個(gè)Click事件對吧,對設(shè)置 OnClickListener 來說, Button 是被觀察者皿哨, OnClickListener 是觀察者,二者通過 setOnClickListener() 方法產(chǎn)生關(guān)系椎例。
OnClickListener一直觀察著Button挨决,當(dāng)Button被點(diǎn)擊,OnClickListener執(zhí)行onClick事件订歪。
RxJava 的觀察者模式脖祈,與之類似,Observable (可觀察者刷晋,即被觀察者)盖高、 Observer (觀察者)、 subscribe (訂閱)眼虱、事件喻奥。Observable 和 Observer 通過 subscribe() 方法實(shí)現(xiàn)訂閱關(guān)系,從而 Observable 可以在需要的時(shí)候發(fā)出事件來通知 Observer捏悬∽膊希可以通過下表進(jìn)行理解:

點(diǎn)擊事件 RxJava
Button Observable
OnClickListener Observer
setOnClickListener() subscribe()
onClick() onNext() onCompleted() onError()

簡單實(shí)現(xiàn)

下面舉一個(gè)基本的例子:

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

當(dāng)傳入的是一個(gè)數(shù)組的時(shí)候过牙,可以使用from方法

 String[] words = {"aaaa", "bbbb", "cccc"};
        Observable observable = Observable.from(words);
        observable.subscribe(observer);

subscribe()

上面提到的都是去subscribe一個(gè)observer甥厦,在observer中也是一個(gè)一個(gè)的任務(wù),如果單獨(dú)執(zhí)行一個(gè)任務(wù)是否可以呢寇钉?答案是:必須的刀疙。

 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);

結(jié)果如下:

Paste_Image.png

線程管理Scheduler

在不指定線程的情況下, RxJava 遵循的是線程不變的原則扫倡,即:在哪個(gè)線程調(diào)用 subscribe()谦秧,就在哪個(gè)線程生產(chǎn)事件;在哪個(gè)線程生產(chǎn)事件,就在哪個(gè)線程消費(fèi)事件油够。如果需要切換線程蚁袭,就需要用到 Scheduler (調(diào)度器)征懈。

  • Schedulers.immediate(): 直接在當(dāng)前線程運(yùn)行石咬,相當(dāng)于不指定線程。這是默認(rèn)的 Scheduler卖哎。

  • Schedulers.newThread(): 總是啟用新線程鬼悠,并在新線程執(zhí)行操作。

  • Schedulers.io(): I/O 操作(讀寫文件亏娜、讀寫數(shù)據(jù)庫焕窝、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler。行為模式和 newThread() 差不多维贺,區(qū)別在于 io() 的內(nèi)部實(shí)現(xiàn)是是用一個(gè)無數(shù)量上限的線程池它掂,可以重用空閑的線程,因此多數(shù)情況下 io() 比 newThread() 更有效率溯泣。不要把計(jì)算工作放在 io() 中虐秋,可以避免創(chuàng)建不必要的線程。

  • Schedulers.computation(): 計(jì)算所使用的 Scheduler垃沦。這個(gè)計(jì)算指的是 CPU 密集型計(jì)算客给,即不會被 I/O 等操作限制性能的操作,例如圖形的計(jì)算肢簿。這個(gè) Scheduler 使用的固定的線程池靶剑,大小為 CPU 核數(shù)。不要把 I/O 操作放在 computation() 中池充,否則 I/O 操作的等待時(shí)間會浪費(fèi) CPU桩引。

  • 另外, Android 還有一個(gè)專用的AndroidSchedulers.mainThread()收夸,它指定的操作將在 Android 主線程運(yùn)行阐污。

圖片下載

對于Android來說異步線程與主線程交互,是一個(gè)關(guān)鍵點(diǎn)咱圆,這里舉個(gè)簡單的例子:

 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() 發(fā)生在 IO 線程
        .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回調(diào)發(fā)生在主線程
                ;
        observable.subscribe(observer);

其中 Bitmap bitmap = binary2Bitmap(getNetData(imageurl));是獲取網(wǎng)絡(luò)圖片笛辟,具體實(shí)現(xiàn)方法可以參照我的demo,但是這個(gè)方法由于有網(wǎng)絡(luò)請求序苏,需要放到子線程手幢,所以使用Schedulers.io(),而回調(diào)需要設(shè)置ImageView忱详,需要放到主線程围来,所以使用AndroidSchedulers.mainThread()

map

map是做什么用的呢,我們可以這樣理解,我們希望傳入的類型是String類型监透,而處理的類型是int類型桶错,這時(shí)應(yīng)該怎么辦呢,需要在處理之前胀蛮,根據(jù)一定的邏輯院刁,將string轉(zhuǎn)成int型。

 Observable.just("aaaa","bbb","cc") // 輸入類型 String
                .map(new Func1<String, Integer>() {
                    @Override
                    public Integer call(String s) { // 參數(shù)類型 String
                        return s.length(); // 返回類型 Bitmap
                    }
                })
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer i) { // 參數(shù)類型 Bitmap
                        Log.e(tag,"length = "+i);
                    }
                });

輸出:

flatMap

關(guān)于flatMap的使用場景有點(diǎn)抽象粪狼,我先上代碼退腥,然后再介紹:

 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) { // 參數(shù)類型 Bitmap
                        Log.e(tag,"length = "+i);
                    }
                });

打印截圖:

我們可以這樣理解, flatMap() 中返回的是個(gè) Observable 對象再榄,并且這個(gè) Observable 對象并不是被直接發(fā)送到了 Subscriber 的回調(diào)方法中狡刘。
例如上面的例子,可以看做是困鸥,一個(gè)String返回了一個(gè)Observable嗅蔬,一個(gè)Observable執(zhí)行了三次Action。

filter

顧名思義疾就,filter就是一個(gè)過濾澜术,可以指定過濾條件,例如我指定過濾字符串中含有字符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);

輸出結(jié)果:

好了基本就講這么多虐译,如果感興趣的用戶可以下載我的demo瘪板,自己修改些內(nèi)容測試一下,理解可能會更深刻漆诽。
我的demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侮攀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子厢拭,更是在濱河造成了極大的恐慌兰英,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件供鸠,死亡現(xiàn)場離奇詭異畦贸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)楞捂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門杆怕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來子刮,“玉大人,你說我怎么就攤上這事≈豁铮” “怎么了座柱?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵榴鼎,是天一觀的道長口叙。 經(jīng)常有香客問我乡数,道長,這世上最難降的妖魔是什么闻牡? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任净赴,我火速辦了婚禮,結(jié)果婚禮上罩润,老公的妹妹穿的比我還像新娘玖翅。我一直安慰自己,他們只是感情好哨啃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布烧栋。 她就那樣靜靜地躺著写妥,像睡著了一般拳球。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上珍特,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天祝峻,我揣著相機(jī)與錄音,去河邊找鬼扎筒。 笑死莱找,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嗜桌。 我是一名探鬼主播奥溺,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼骨宠!你這毒婦竟也來了浮定?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤层亿,失蹤者是張志新(化名)和其女友劉穎桦卒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匿又,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡方灾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碌更。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裕偿。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖痛单,靈堂內(nèi)的尸體忽然破棺而出嘿棘,到底是詐尸還是另有隱情,我是刑警寧澤桦他,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布蔫巩,位于F島的核電站谆棱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏圆仔。R本人自食惡果不足惜垃瞧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坪郭。 院中可真熱鬧个从,春花似錦、人聲如沸歪沃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沪曙。三九已至奕污,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間液走,已是汗流浹背碳默。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缘眶,地道東北人嘱根。 一個(gè)月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像巷懈,于是被迫代替她去往敵國和親该抒。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容