Rxjava2-小白入門(二)

前言

上篇文章我們主要講解了觀察者模式。那麼這節課我們主要講解Rxjava2的基本使用和操作符。其實網上的關於Rxjava2的優秀文章有很多對我來說都是受益匪淺。可能我寫的文章和他們比相差很遠,但是我也不能灰心能幫助一個人是一個人就算不能幫助別人我也會把文章當作筆記,閒暇時好好閱讀,畢竟人家寫的是人家的自己總結的纔是自己的。


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


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

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

這是我使用的Rxjava2版本最新的請查閱官網

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

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

Rxjava所有操作符文檔


Rxjava2的操作符

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

Rxjava2的使用場景

  • 登陸後獲取用戶信息
  • 關鍵詞搜索
  • 防止按鈕重複點擊
  • 購物車合併本地和網絡數據
  • 發送驗證碼倒計時

瞭解本文的大致內容我們先一步一步來。首先我們先了解如何創建。

創建訂閱關係

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

Observable 和 Observer 通過 subscribe() 方法實現訂閱關係

在瞭解關係後我們來學習幾種創建方式,首先我們先學習一種最簡單的創建方式:

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

輸出結果:

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()創建,那麼ObservableEmitter是什麼呢?它是指發射器的意思它裏面有onNext(),onComplete(),onError()(注意onComplete和onError兩者是互斥的,不能同時發送),三個方法分別代表發送數據,發送結束,發送錯誤。其中的指的是我們發送數據的類型。最後我們通過subscribe將2者關係進行訂閱(注意只有訂閱的時候纔會發送數據)。從打印的中我們可以發現對應的Observer也有相對應的3個方法。所以你發送的是 e.onNext()就會進入public void onNext(String value)其他的也是一樣的。

細心的小夥伴可能會發現,每次接受都會先走到onSubscribe方法中,那麼這個方法是幹什麼的呢?
其實他有Disposable d這個參數,他裏面只有兩個重要的方法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: 睡覺

當符合條件後調用dd.dispose();後面的數據就不在接受了。

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

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

這幾種方法都可以簡單的創建Observable對象。那麼我們在來學習下Observer的創建方法:

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
這次我們創建Observer採用的是鏈式創建,通過參數我們可以看到其實這種創建方法和我們之前創建的方式意思是一樣的,只不過它是分來了的,因爲通常的時候我們一般都是在onNext中去接受數據那麼我就開一單獨創建一個Consumer()這樣使用起來更加方便。


Scheduler線程控制

我們簡單的學習了創建訂閱關係(和鏈式創建),那麼我們再來學習另一個Rxjava2的重要內容,就是線程控制。

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。

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

我認爲Rxjava2的強大之處就在與它有很多的操作符可以根據業務邏輯的需求通過Rxjava2鏈式不斷的變化來滿足我們的需求,另一個就是它可以制定任意Observer和Observa的業務邏輯在那個線程中執行(當然它的強大之處太多了比如生命週期的管理,背壓緩衝區等等)那麼我們通過實際的例子來了解下吧!

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

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

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

Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同樣是在 subscribe() 調用後而且在事件發送前執行,但區別在於它可以指定線程


案例

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

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

Map操作符對原始Observable發射的每一項數據應用一個你選擇的函數,然後返回一個發射這些結果的Observable

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

                Observable<String> map = just.map(new Function<Integer, String>() {
                    /**
                     * map返回的也是一個Observable<String>
                     * @param integer 傳入的類型
                     * @return 返回結果爲字符串
                     */
                    @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);
                    }
                });
            }
        });

結果:

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

可以看到我們發射的原始數據是Integer通過map操作符後我們將Integer轉成了字符串 我們在接受到的結果也是(String s)字符串類型的。這就map操作符的作用。當然實際應用中它可不是簡單的這麼使用的。下面我們看一個實際的登錄例子:

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

在這個例子中我們用到flatmap:
圖片.png
圖片.png

        int userId = 111111;
        Observable.just(userId).flatMap(new Function<Integer, ObservableSource<Result>>() {
            @Override
            public ObservableSource<Result> apply(Integer integer) throws Exception {
                /**
                 *模擬網絡請求
                 *通過userId登錄 
                 * 登錄成功返回結果result
                 */
                Result result = null;//這裏是通過傳入的userId作爲請求參數請求網絡返回用戶信息
                return Observable.just(result);
            }
        }).flatMap(new Function<Result, ObservableSource<User>>() {
            @Override
            public ObservableSource<User> apply(Result result) throws Exception {
                /**
                 *模擬網絡請求
                 * 根據返回的登錄結果result
                 * 請求包含用戶的帳號 年齡等個人信息User
                 */
                final User user = null;//這裏是通過傳入的result作爲請求參數請求網絡返回用戶信息其他信息
                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我們可以很方便的實現我們登錄的業務邏輯需求。通過鏈式書寫將所有操作一起完成,如果還有複雜的請求我們可以繼續往下寫。

爲什麼在等落的時候我們用的是flatmap而不是map呢?
比較會發現map返回的是基本數據類型或者是Object,而flatmap返回是的ObservableSource<?>,那麼我就可以調用操作符再做處理,而map是數據類型不能再做其他處理了。多比較使用就會更好的理解。

總結:這篇文章已經不短了。。我在閱讀文章的時候就不喜歡長的文章。所以剩下的例子和操作符我會在寫一篇。這篇就到這裏了,大家也可以參考下菜鳥窩Iavn老師的免費課程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章