本篇文章已授權(quán)微信公眾號 YYGeeker
獨家發(fā)布轉(zhuǎn)載請標(biāo)明出處
CSDN學(xué)院課程地址
- RxJava2從入門到精通-初級篇:https://edu.csdn.net/course/detail/10036
- RxJava2從入門到精通-中級篇:https://edu.csdn.net/course/detail/10037
- RxJava2從入門到精通-進階篇:https://edu.csdn.net/course/detail/10038
- RxJava2從入門到精通-源碼分析篇:https://edu.csdn.net/course/detail/10138
6. RxJava基礎(chǔ)實戰(zhàn)
6.1 模擬發(fā)送驗證碼
應(yīng)用場景:當(dāng)用戶點擊發(fā)送驗證碼后,在倒計時的時間內(nèi)是不可以重新點擊發(fā)送驗證碼的,倒計時結(jié)束后,發(fā)送驗證碼的按鈕重新恢復(fù)點擊,這里舉例子為3s的倒計時
public void verify(View view) {
final long count = 3;//倒計時時間
final Button button = (Button) view;//當(dāng)前按鈕
Observable.interval(0, 1, TimeUnit.SECONDS)//定時器
.take(count + 1)//取定時器前4個讨便,當(dāng)前值:0带迟,1爬立,2命锄,3
.map(new Function<Long, Long>() {
@Override
public Long apply(@NonNull Long aLong) throws Exception {
return count - aLong;//將值轉(zhuǎn)換下堰乔,當(dāng)前值:3,2脐恩,1镐侯,0
}
})
.observeOn(AndroidSchedulers.mainThread())//主線程更新UI
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(@NonNull Disposable disposable) throws Exception {
//監(jiān)聽訂閱時,將按鈕設(shè)置為不可點擊
button.setEnabled(false);
button.setTextColor(Color.BLACK);
}
})
.subscribe(new Observer<Long>() {
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(Long aLong) {
//設(shè)置倒計時文本
button.setText("剩余" + aLong + "秒");
}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {
//事件完成后恢復(fù)點擊
button.setEnabled(true);
button.setText("發(fā)送驗證碼");
}
});
}
6.2 模擬用戶點擊防抖動
應(yīng)用場景:在某些應(yīng)用場景中驶冒,用戶會多次點擊同一個按鈕苟翻,導(dǎo)致有多次點擊事件的產(chǎn)生,如果點擊事件中是網(wǎng)絡(luò)請求骗污,那么就會產(chǎn)生多次網(wǎng)絡(luò)請求崇猫。正確的操作應(yīng)該是,在一定時間內(nèi)需忿,用戶頻繁點擊多次按鈕之后邓尤,只訪問一次網(wǎng)絡(luò)請求。下面針對所說的需求進行編寫
寫法一:
/**
* 模擬用戶點擊防抖動
*/
public void query(View view) {
RxUtils.click(view, 2)
.subscribe(new Observer<Object>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Object o) {
System.out.println("onNext");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
System.out.println("onComplete");
}
});
}
//封裝工具
static class RxUtils {
static Observable<Object> click(final View view, long seconds) {
return new ViewClickObservable(view)
.throttleFirst(seconds, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnDispose(new Action() {
@Override
public void run() throws Exception {
if (view != null) {
view.setOnClickListener(null);
}
}
}
);
}
}
//創(chuàng)建一個觀察者
static class ViewClickObservable extends Observable<Object> {
private View view;
public ViewClickObservable(View view) {
this.view = view;
}
//當(dāng)這個觀察者被訂閱的時候贴谎,會執(zhí)行下面的回調(diào)
@Override
protected void subscribeActual(final Observer<? super Object> observer) {
if (view != null) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
observer.onNext(v);
}
});
}
}
}
寫法二:
static class ViewClickObservableOnSubscribe implements ObservableOnSubscribe<Object> {
private ObservableEmitter<Object> emitter;
public ObservableEmitter<Object> getEmitter() {
return emitter;
}
@Override
public void subscribe(ObservableEmitter<Object> e) throws Exception {
this.emitter = e;
}
}
//封裝工具
static class RxUtils {
static Observable<Object> clicks(final View view, long seconds) {
final ViewClickObservableOnSubscribe viewClickObservableOnSubscribe = new ViewClickObservableOnSubscribe();
if (view != null) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObservableEmitter<Object> emitter = viewClickObservableOnSubscribe.getEmitter();
if (emitter != null && !emitter.isDisposed()) {
emitter.onNext(1);
}
}
});
}
return Observable
.create(viewClickObservableOnSubscribe)
.throttleFirst(seconds, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnDispose(new Action() {
@Override
public void run() throws Exception {
if (view != null) {
view.setOnClickListener(null);
}
}
}
);
}
}
/**
* 模擬用戶點擊防抖動
*/
public void query(View view) {
RxUtils.clicks(view, 2)
.subscribe(new Observer<Object>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Object o) {
System.out.println("onNext");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
System.out.println("onComplete");
}
});
}
6.3 模擬會員信息的合并
應(yīng)用場景:假如我們當(dāng)前在好友聊天列表
界面中汞扎,客戶端需要通過好友列表的uid
去查詢好友列表中會員信息
,然后顯示會員圖標(biāo)等信息擅这。假如聊天列表界面中的好友數(shù)量有成千上百個澈魄,客戶端每次從后臺批量查詢用戶會員信息后,需要在本地做緩存仲翎,如果這時用戶在聊天列表中進入某個群聊
界面痹扇,這個時候還是需要去獲取群聊中
的所有用戶的會員信息,如果群聊界面
中也包含有自己的好友溯香,那么我們就會去判斷鲫构,如果用戶的會員信息在緩存中存在,則從緩存中獲取玫坛,如果在緩存中不存在结笨,則加入到一個請求集合中,批量查詢會員信息后湿镀,合并本地緩存的會員信息和新的服務(wù)器的會員信息炕吸,將信息返回給群聊界面
1、創(chuàng)建會員實體類
private HashMap<Long, Vip> mVipCache = new HashMap<>();//作為緩存的類型
public static class Vip {
//會員信息實體類
}
2勉痴、模擬從本地獲取數(shù)據(jù)
- 遍歷批量的
uid
參數(shù)赫模,從緩存和網(wǎng)絡(luò)中獲取會員信息的數(shù)據(jù) - 如果本地數(shù)據(jù)存在,則需要將緩存的會員信息加入到
vipInfo
- 如果本地數(shù)據(jù)不存在蒸矛,則需要將
uid
加入到請求列表mRequestList
- 最后合并本地緩存數(shù)據(jù)和網(wǎng)絡(luò)請求數(shù)據(jù)
/**
* 從本地緩存中獲取數(shù)據(jù)
*/
public Observable<HashMap<Long, Vip>> getVipFromCache(List<Long> uids) {
List<Long> mRequestList = new ArrayList<>();
HashMap<Long, Vip> vipInfo = new HashMap<>();
for (Long uid : uids) {
if (mVipCache.containsKey(uid)) {
Log.e("TAG", "從本地獲取數(shù)據(jù):" + uid + "用戶");
vipInfo.put(uid, mVipCache.get(uid));//如果緩存中有數(shù)據(jù)瀑罗,則從本地中取出
} else {
Log.e("TAG", "從網(wǎng)絡(luò)獲取數(shù)據(jù):" + uid + "用戶");
mRequestList.add(uid);//如果緩存中沒有數(shù)據(jù)胸嘴,則加入到網(wǎng)絡(luò)請求列表中
}
}
if (mRequestList.isEmpty()) {
//如果請求列表中為空,則直接返回緩存的數(shù)據(jù)
return Observable.just(vipInfo);
}
//合并緩存的數(shù)據(jù)和網(wǎng)絡(luò)獲取的數(shù)據(jù)
return Observable.merge(Observable.just(vipInfo), getVipFromWeb(mRequestList));
}
3斩祭、模擬從服務(wù)器獲取數(shù)據(jù)
- 睡眠2s鐘劣像,用于模擬網(wǎng)絡(luò)請求的耗時時間
- 模擬后臺返回的數(shù)據(jù),并加入到緩存列表中
- 返回新的事件流
/**
* 從網(wǎng)絡(luò)上批量查詢Vip信息
*/
public Observable<HashMap<Long, Vip>> getVipFromWeb(List<Long> uids) {
//由于這里沒有對應(yīng)的接口停忿,所以模擬請求網(wǎng)絡(luò)數(shù)據(jù)
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//模擬返回的數(shù)據(jù)
HashMap<Long, Vip> vip = new HashMap<>();
for (Long uid : uids) {
//后臺返回的數(shù)據(jù)進行賦值
Vip vipInfo = new Vip();
//vipInfo.xxx = WebValue;
//vipInfo.xxx = WebValue;
vip.put(uid, vipInfo);
}
//緩存到本地
mVipCache.putAll(vip);
return Observable.just(vip);
}
4、模擬合并本地數(shù)據(jù)和服務(wù)器數(shù)據(jù)
- 模擬應(yīng)用場景蚊伞,查詢當(dāng)前好友列表的會員信息
- 模擬應(yīng)用場景席赂,進入群聊頁面,查詢?nèi)毫牧斜淼臅T信息
- 模擬應(yīng)用場景时迫,定時更新會員的信息
/**
* 模擬合并本地信息和服務(wù)器信息
*/
public void vip(View view) {
List<Long> uids = new ArrayList<>();
Log.e("TAG", "第一次查詢颅停,進入好友列表界面,查詢好友列表的會員信息");
uids.add(1L);
uids.add(2L);
getVipFromCache(uids);
uids.clear();
Log.e("TAG", "第二次查詢掠拳,進入群聊界面癞揉,查詢?nèi)毫闹械臅T信息");
uids.add(1L);
uids.add(3L);
uids.add(4L);
getVipFromCache(uids);
uids.clear();
Log.e("TAG", "第三次查詢,定時更新最新會員信息溺欧,更新所有緩存里的數(shù)據(jù)");
uids.add(1L);
uids.add(2L);
uids.add(3L);
uids.add(4L);
mVipCache.clear();
getVipFromCache(uids);
}
5喊熟、輸出結(jié)果
達(dá)到我們預(yù)期的設(shè)想和最優(yōu)的解決方案
第一次查詢,進入好友列表界面姐刁,查詢好友列表的會員信息
從網(wǎng)絡(luò)獲取數(shù)據(jù):1用戶
從網(wǎng)絡(luò)獲取數(shù)據(jù):2用戶
第二次查詢芥牌,進入群聊界面,查詢?nèi)毫闹械臅T信息
從本地獲取數(shù)據(jù):1用戶
從網(wǎng)絡(luò)獲取數(shù)據(jù):3用戶
從網(wǎng)絡(luò)獲取數(shù)據(jù):4用戶
第三次查詢聂使,定時更新最新會員信息壁拉,更新所有緩存里的數(shù)據(jù)
從網(wǎng)絡(luò)獲取數(shù)據(jù):1用戶
從網(wǎng)絡(luò)獲取數(shù)據(jù):2用戶
從網(wǎng)絡(luò)獲取數(shù)據(jù):3用戶
從網(wǎng)絡(luò)獲取數(shù)據(jù):4用戶