Rxjava2-小白入門(二)

前言

上篇文章我們主要講解了觀察者模式。那么這節(jié)課我們主要講解Rxjava2的基本使用和操作符。其實網(wǎng)上的關(guān)于Rxjava2的優(yōu)秀文章有很多對我來說都是受益匪淺。可能我寫的文章和他們比相差很遠(yuǎn),但是我也不能灰心能幫助一個人是一個人就算不能幫助別人我也會把文章當(dāng)作筆記,閑暇時好好閱讀,畢竟人家寫的是人家的自己總結(jié)的才是自己的。


上篇文章提到,說要簡單介紹下Android中的觀察者模式,其實在java中也有兩個類Observer和Observable兩個類其實他們的類的主要內(nèi)容和我們上節(jié)課寫的大致都是相同的,有興趣的同學(xué)可以自己去了解下,在這里我就不加介紹了。這篇我們主要是講解Rxjava2的簡單用法已經(jīng)場景


首先我們先在我們的項目中添加依賴

compile 'io.reactivex.rxjava2:rxjava:2.0.0-RC5'
compile 'io.reactivex.rxjava2:rxandroid:2.0.0-RC1'

這是我使用的Rxjava2版本最新的請查閱官網(wǎng)

RxJava:   https://github.com/ReactiveX/RxJava 
RxAndroid : https://github.com/ReactiveX/RxAndroid 

我把2者的官網(wǎng)發(fā)出來了大家有興趣的可以大家了解
有的人會問RxAndroid是什么?不是講Rxjava2嗎?其實RxAndroid,這是一個擴展庫,更好的兼容了Android特性,比如主線程,UI事件等。我在把Rxjava文檔給大家發(fā)出來方便大家對Rxjava更多的了解

Rxjava所有操作符文檔


Rxjava2的操作符

  • create
  • just
  • fromArray
  • map
  • flatMap
  • zip
  • filter
  • time
  • merge
  • retry
  • retryWhen
  • range
  • Interval
  • ...

Rxjava2的使用場景

  • 登陸后獲取用戶信息
  • 關(guān)鍵詞搜索
  • 防止按鈕重復(fù)點擊
  • 購物車合并本地和網(wǎng)絡(luò)數(shù)據(jù)
  • 發(fā)送驗證碼倒計時

了解本文的大致內(nèi)容我們先一步一步來。首先我們先了解如何創(chuàng)建。

創(chuàng)建訂閱關(guān)系

  1. Observable:被觀察者(主題Subject)
  2. Observer/Subscriber :觀察者
  3. Subscribe:訂閱

Observable 和 Observer 通過 subscribe() 方法實現(xiàn)訂閱關(guān)系

在了解關(guān)系后我們來學(xué)習(xí)幾種創(chuàng)建方式,首先我們先學(xué)習(xí)一種最簡單的創(chuàng)建方式:

package com.example.ggxiaozhi.rxjava;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private Button mButton;

    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.btn);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Observable<String> observable = getObservable();
                Observer<String> observer = getObserver();
                observable.subscribe(observer); 
            }
        });
    }

    public Observable<String> getObservable() {
        return Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext("吃飯");
                e.onNext("睡覺");
                e.onNext("打豆豆");
                e.onComplete();
//                e.onError(new Throwable("錯誤"));
            }
        });
    }

    public Observer<String> getObserver() {
        return new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.d(TAG, "onSubscribe: ");
            }

            @Override
            public void onNext(String value) {
                Log.d(TAG, "onNext: " + value);
            }

            @Override
            public void onError(Throwable e) {
                Log.d(TAG, "onError: " + e.getMessage());
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "onComplete: ");
            }
        };
    }
}

輸出結(jié)果:

10-08 16:23:05.368 4767-4767/com.example.ggxiaozhi.rxjava D/MainActivity: onSubscribe: 
10-08 16:23:05.368 4767-4767/com.example.ggxiaozhi.rxjava D/MainActivity: onNext: 吃飯
10-08 16:23:05.368 4767-4767/com.example.ggxiaozhi.rxjava D/MainActivity: onNext: 睡覺
10-08 16:23:05.368 4767-4767/com.example.ggxiaozhi.rxjava D/MainActivity: onNext: 打豆豆
10-08 16:23:05.368 4767-4767/com.example.ggxiaozhi.rxjava D/MainActivity: onComplete: 

使用 e.onError(new Throwable("錯誤"));

10-08 16:25:17.948 6894-6894/com.example.ggxiaozhi.rxjava D/MainActivity: onSubscribe: 
10-08 16:25:17.998 6894-6894/com.example.ggxiaozhi.rxjava D/MainActivity: onNext: 吃飯
10-08 16:25:17.998 6894-6894/com.example.ggxiaozhi.rxjava D/MainActivity: onNext: 睡覺
10-08 16:25:17.998 6894-6894/com.example.ggxiaozhi.rxjava D/MainActivity: onNext: 打豆豆
10-08 16:25:17.998 6894-6894/com.example.ggxiaozhi.rxjava D/MainActivity: onError: 錯誤

我們來分析下這段代碼 在Observable中我們通過Observable.create()創(chuàng)建,那么ObservableEmitter是什么呢?它是指發(fā)射器的意思它里面有onNext(),onComplete(),onError()(注意onComplete和onError兩者是互斥的,不能同時發(fā)送),三個方法分別代表發(fā)送數(shù)據(jù),發(fā)送結(jié)束,發(fā)送錯誤。其中的<String>指的是我們發(fā)送數(shù)據(jù)的類型。最后我們通過subscribe將2者關(guān)系進行訂閱(注意只有訂閱的時候才會發(fā)送數(shù)據(jù))。從打印的中我們可以發(fā)現(xiàn)對應(yīng)的Observer也有相對應(yīng)的3個方法。所以你發(fā)送的是 e.onNext()就會進入public void onNext(String value)其他的也是一樣的。

細(xì)心的小伙伴可能會發(fā)現(xiàn),每次接受都會先走到onSubscribe方法中,那么這個方法是干什么的呢?
其實他有Disposable d這個參數(shù),他里面只有兩個重要的方法d.dispose();d.isDisposed();一個是用來阻斷接受,另一個是用于判斷的我們簡單來試用下:

    Disposable dd;
    public Observer<String> getObserver() {
        
        return new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                dd=d;
                Log.d(TAG, "onSubscribe: ");
            }

            @Override
            public void onNext(String value) {
                if ("睡覺".equals(value)){
                    dd.dispose();
                }
                Log.d(TAG, "onNext: " + value);
            }

            @Override
            public void onError(Throwable e) {
                Log.d(TAG, "onError: " + e.getMessage());
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "onComplete: ");
            }
        };
    }
10-08 16:35:16.158 14943-14943/com.example.ggxiaozhi.rxjava D/MainActivity: onSubscribe: 
10-08 16:35:16.158 14943-14943/com.example.ggxiaozhi.rxjava D/MainActivity: onNext: 吃飯
10-08 16:35:16.158 14943-14943/com.example.ggxiaozhi.rxjava D/MainActivity: onNext: 睡覺

當(dāng)符合條件后調(diào)用dd.dispose();后面的數(shù)據(jù)就不在接受了。

那么有沒有其他的創(chuàng)建方法呢?答案是肯定的。下面我們來使用其他操作符進行創(chuàng)建:

 public Observable<String> getObservable() {
        String[] strings={"吃飯","睡覺","打豆豆"};
        return Observable.fromArray(strings);
    }
return Observable.just("吃飯","睡覺","打豆豆");

這幾種方法都可以簡單的創(chuàng)建Observable對象。那么我們在來學(xué)習(xí)下Observer的創(chuàng)建方法:

observable.subscribe(new Consumer<String>() {
                    @Override
                    public void accept(String s) throws Exception {

                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {

                    }
                }, new Action() {
                    @Override
                    public void run() throws Exception {

                    }
                }, new Consumer<Disposable>() {
                    @Override
                    public void accept(Disposable disposable) throws Exception {
                        
                    }
                });
圖片.png

這次我們創(chuàng)建Observer采用的是鏈?zhǔn)絼?chuàng)建,通過參數(shù)我們可以看到其實這種創(chuàng)建方法和我們之前創(chuàng)建的方式意思是一樣的,只不過它是分來了的,因為通常的時候我們一般都是在onNext中去接受數(shù)據(jù)那么我就開一單獨創(chuàng)建一個Consumer()這樣使用起來更加方便。


Scheduler線程控制

我們簡單的學(xué)習(xí)了創(chuàng)建訂閱關(guān)系(和鏈?zhǔn)絼?chuàng)建),那么我們再來學(xué)習(xí)另一個Rxjava2的重要內(nèi)容,就是線程控制。

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

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

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

Schedulers.computation():
計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操作限制性能的操作,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小為 CPU 核數(shù)。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU。

AndroidSchedulers.mainThread():
它指定的操作將在 Android 主線程運行。

我認(rèn)為Rxjava2的強大之處就在與它有很多的操作符可以根據(jù)業(yè)務(wù)邏輯的需求通過Rxjava2鏈?zhǔn)讲粩嗟淖兓瘉頋M足我們的需求,另一個就是它可以制定任意Observer和Observa的業(yè)務(wù)邏輯在那個線程中執(zhí)行(當(dāng)然它的強大之處太多了比如生命周期的管理,背壓緩沖區(qū)等等)那么我們通過實際的例子來了解下吧!

     Observable.create(new ObservableOnSubscribe<String>() {
                    @Override
                    public void subscribe(ObservableEmitter<String> e) throws Exception {
                        //在這里我們進行網(wǎng)絡(luò)請求 請求結(jié)果返回一個字符串
                        e.onNext("網(wǎng)絡(luò)請求結(jié)果");
                    }
                }).subscribeOn(Schedulers.io()).
                        observeOn(AndroidSchedulers.mainThread()).
                        subscribe(new Consumer<String>() {
                            @Override
                            public void accept(String s) throws Exception {
                                Log.d(TAG, "accept: " + s);
                            }
                        });
            }
        });

這就是我們常用的在Observable發(fā)送數(shù)據(jù)前我們先請求網(wǎng)絡(luò)(一般我們請求網(wǎng)絡(luò)都會返回一個Json字符串或是實體類)然后將等到的消息發(fā)給Consumer()(Observer),我們都知道Android中是不允許在主線程請求網(wǎng)絡(luò)操作的,并且通常我們請求的到結(jié)果是用來給UI控件賦值的,那么Rxjava中的線程控制就很好的幫住了我們解決這個問題,我們通過subscribeOn()這是制定Observable在那個線程執(zhí)行,通過observeOn指定Consumer()運行在主線程從而更新UI(一定要記得切回主線程,因為你開啟了子線程請求網(wǎng)絡(luò),如果不切回主線程的話默認(rèn)還是在請求網(wǎng)絡(luò)的子線程的那么是無法更新UI的)。相信大家也能明白,如果不制定線程所有操作都是在主線程中運行的。

observeOn() 指定 Subscriber 線程
subscribeOn 制定 Observable 線程

Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同樣是在 subscribe() 調(diào)用后而且在事件發(fā)送前執(zhí)行,但區(qū)別在于它可以指定線程


案例

在了解了Rxjava2的原理,創(chuàng)建和使用下面我就該學(xué)習(xí)他的操作符了,在文章的開頭我已經(jīng)把Rxjava操作符文檔發(fā)出來了,大家會發(fā)現(xiàn)他的操作符實在是太!多!了!。如果真的想去精通了解每個操作符想必也是有些太費時間,本文是小白入門,那么我們通過結(jié)合實例來學(xué)習(xí)操作符會更容易理解和記憶

  • 登陸后獲取用戶信息(flatMap)
    首先我們先了解下map操作符的定義:

Map操作符對原始Observable發(fā)射的每一項數(shù)據(jù)應(yīng)用一個你選擇的函數(shù),然后返回一個發(fā)射這些結(jié)果的Observable

 Observable<Integer> just = Observable.just(1);

                Observable<String> map = just.map(new Function<Integer, String>() {
                    /**
                     * map返回的也是一個Observable<String>
                     * @param integer 傳入的類型
                     * @return 返回結(jié)果為字符串
                     */
                    @Override
                    public String apply(Integer integer) throws Exception {
                        
                        return integer + "value";
                    }
                });
                map.subscribe(new Consumer<String>() {
                    @Override
                    public void accept(String s) throws Exception {
                        Log.d(TAG, "accept: " + s);
                    }
                });
            }
        });

結(jié)果:

10-08 18:45:21.588 31174-31174/com.example.ggxiaozhi.rxjava D/MainActivity: accept: 1value

可以看到我們發(fā)射的原始數(shù)據(jù)是Integer通過map操作符后我們將Integer轉(zhuǎn)成了字符串 我們在接受到的結(jié)果也是(String s)字符串類型的。這就map操作符的作用。當(dāng)然實際應(yīng)用中它可不是簡單的這么使用的。下面我們看一個實際的登錄例子:

我們傳入我們的用戶ID,通過這個ID等落成功后返回一個結(jié)果,我們再通過這個結(jié)果查詢用戶的其他信息

在這個例子中我們用到flatmap:



        int userId = 111111;
        Observable.just(userId).flatMap(new Function<Integer, ObservableSource<Result>>() {
            @Override
            public ObservableSource<Result> apply(Integer integer) throws Exception {
                /**
                 *模擬網(wǎng)絡(luò)請求
                 *通過userId登錄 
                 * 登錄成功返回結(jié)果result
                 */
                Result result = null;//這里是通過傳入的userId作為請求參數(shù)請求網(wǎng)絡(luò)返回用戶信息
                return Observable.just(result);
            }
        }).flatMap(new Function<Result, ObservableSource<User>>() {
            @Override
            public ObservableSource<User> apply(Result result) throws Exception {
                /**
                 *模擬網(wǎng)絡(luò)請求
                 * 根據(jù)返回的登錄結(jié)果result
                 * 請求包含用戶的帳號 年齡等個人信息User
                 */
                final User user = null;//這里是通過傳入的result作為請求參數(shù)請求網(wǎng)絡(luò)返回用戶信息其他信息
                return Observable.create(new ObservableOnSubscribe<User>() {
                    @Override
                    public void subscribe(ObservableEmitter<User> e) throws Exception {
                        e.onNext(user);
                    }
                });
            }
        }).subscribeOn(Schedulers.io()).
                observeOn(AndroidSchedulers.mainThread()).
                subscribe(new Consumer<User>() {
                    @Override
                    public void accept(User user) throws Exception {
                        /**
                         * 通過返回的User等到name更新UI
                         */
                        Log.d(TAG, "accept: " + user.getName());
                    }
                });
    }

通過flatmap我們可以很方便的實現(xiàn)我們登錄的業(yè)務(wù)邏輯需求。通過鏈?zhǔn)綍鴮憣⑺胁僮饕黄鹜瓿桑绻€有復(fù)雜的請求我們可以繼續(xù)往下寫。

為什么在等落的時候我們用的是flatmap而不是map呢?
比較會發(fā)現(xiàn)map返回的是基本數(shù)據(jù)類型或者是Object,而flatmap返回是的ObservableSource<?>,那么我就可以調(diào)用操作符再做處理,而map是數(shù)據(jù)類型不能再做其他處理了。多比較使用就會更好的理解。

總結(jié):這篇文章已經(jīng)不短了。我在閱讀文章的時候就不喜歡長的文章。所以剩下的例子和操作符我會在寫一篇。這篇就到這里了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,991評論 2 374

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