前言
上篇文章我們主要講解了觀察者模式。那么這節(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更多的了解
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)系
- Observable:被觀察者(主題Subject)
- Observer/Subscriber :觀察者
- 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 {
}
});
這次我們創(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)不短了。我在閱讀文章的時候就不喜歡長的文章。所以剩下的例子和操作符我會在寫一篇。這篇就到這里了。