本篇文章已授權微信公眾號 YYGeeker
獨家釋出轉載請標明出處
CSDN學院課程地址
- RxJava2從入門到精通-初級篇:edu.csdn.net/course/deta…
- RxJava2從入門到精通-中級篇:edu.csdn.net/course/deta…
- RxJava2從入門到精通-進階篇:edu.csdn.net/course/deta…
- RxJava2從入門到精通-原始碼分析篇:edu.csdn.net/course/deta…
6. RxJava基礎實戰
6.1 模擬傳送驗證碼
應用場景:當使用者點選傳送驗證碼後,在倒數計時的時間內是不可以重新點選傳送驗證碼的,倒數計時結束後,傳送驗證碼的按鈕重新恢復點選,這裡舉例子為3s的倒數計時
public void verify(View view) {
final long count = 3;//倒數計時時間
final Button button = (Button) view;//當前按鈕
Observable.interval(0, 1, TimeUnit.SECONDS)//定時器
.take(count + 1)//取定時器前4個,當前值:0,1,2,3
.map(new Function<Long, Long>() {
@Override
public Long apply(@NonNull Long aLong) throws Exception {
return count - aLong;//將值轉換下,當前值:3,2,1,0
}
})
.observeOn(AndroidSchedulers.mainThread())//主執行緒更新UI
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(@NonNull Disposable disposable) throws Exception {
//監聽訂閱時,將按鈕設定為不可點選
button.setEnabled(false);
button.setTextColor(Color.BLACK);
}
})
.subscribe(new Observer<Long>() {
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(Long aLong) {
//設定倒數計時文字
button.setText("剩餘" + aLong + "秒");
}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {
//事件完成後恢復點選
button.setEnabled(true);
button.setText("傳送驗證碼");
}
});
}
複製程式碼
6.2 模擬使用者點選防抖動
應用場景:在某些應用場景中,使用者會多次點選同一個按鈕,導致有多次點選事件的產生,如果點選事件中是網路請求,那麼就會產生多次網路請求。正確的操作應該是,在一定時間內,使用者頻繁點選多次按鈕之後,只訪問一次網路請求。下面針對所說的需求進行編寫
寫法一:
/**
* 模擬使用者點選防抖動
*/
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);
}
}
}
);
}
}
//建立一個觀察者
static class ViewClickObservable extends Observable<Object> {
private View view;
public ViewClickObservable(View view) {
this.view = view;
}
//當這個觀察者被訂閱的時候,會執行下面的回撥
@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 模擬會員資訊的合併
應用場景:假如我們當前在好友聊天列表
介面中,客戶端需要通過好友列表的uid
去查詢好友列表中會員資訊
,然後顯示會員圖示等資訊。假如聊天列表介面中的好友數量有成千上百個,客戶端每次從後臺批量查詢使用者會員資訊後,需要在本地做快取,如果這時使用者在聊天列表中進入某個群聊
介面,這個時候還是需要去獲取群聊中
的所有使用者的會員資訊,如果群聊介面
中也包含有自己的好友,那麼我們就會去判斷,如果使用者的會員資訊在快取中存在,則從快取中獲取,如果在快取中不存在,則加入到一個請求集合中,批量查詢會員資訊後,合併本地快取的會員資訊和新的伺服器的會員資訊,將資訊返回給群聊介面
1、建立會員實體類
private HashMap<Long, Vip> mVipCache = new HashMap<>();//作為快取的型別
public static class Vip {
//會員資訊實體類
}
複製程式碼
2、模擬從本地獲取資料
- 遍歷批量的
uid
引數,從快取和網路中獲取會員資訊的資料 - 如果本地資料存在,則需要將快取的會員資訊加入到
vipInfo
- 如果本地資料不存在,則需要將
uid
加入到請求列表mRequestList
- 最後合併本地快取資料和網路請求資料
/**
* 從本地快取中獲取資料
*/
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", "從本地獲取資料:" + uid + "使用者");
vipInfo.put(uid, mVipCache.get(uid));//如果快取中有資料,則從本地中取出
} else {
Log.e("TAG", "從網路獲取資料:" + uid + "使用者");
mRequestList.add(uid);//如果快取中沒有資料,則加入到網路請求列表中
}
}
if (mRequestList.isEmpty()) {
//如果請求列表中為空,則直接返回快取的資料
return Observable.just(vipInfo);
}
//合併快取的資料和網路獲取的資料
return Observable.merge(Observable.just(vipInfo), getVipFromWeb(mRequestList));
}
複製程式碼
3、模擬從伺服器獲取資料
- 睡眠2s鍾,用於模擬網路請求的耗時時間
- 模擬後臺返回的資料,並加入到快取列表中
- 返回新的事件流
/**
* 從網路上批量查詢Vip資訊
*/
public Observable<HashMap<Long, Vip>> getVipFromWeb(List<Long> uids) {
//由於這裡沒有對應的介面,所以模擬請求網路資料
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//模擬返回的資料
HashMap<Long, Vip> vip = new HashMap<>();
for (Long uid : uids) {
//後臺返回的資料進行賦值
Vip vipInfo = new Vip();
//vipInfo.xxx = WebValue;
//vipInfo.xxx = WebValue;
vip.put(uid, vipInfo);
}
//快取到本地
mVipCache.putAll(vip);
return Observable.just(vip);
}
複製程式碼
4、模擬合併本地資料和伺服器資料
- 模擬應用場景,查詢當前好友列表的會員資訊
- 模擬應用場景,進入群聊頁面,查詢群聊列表的會員資訊
- 模擬應用場景,定時更新會員的資訊
/**
* 模擬合併本地資訊和伺服器資訊
*/
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", "第二次查詢,進入群聊介面,查詢群聊中的會員資訊");
uids.add(1L);
uids.add(3L);
uids.add(4L);
getVipFromCache(uids);
uids.clear();
Log.e("TAG", "第三次查詢,定時更新最新會員資訊,更新所有快取裡的資料");
uids.add(1L);
uids.add(2L);
uids.add(3L);
uids.add(4L);
mVipCache.clear();
getVipFromCache(uids);
}
複製程式碼
5、輸出結果
達到我們預期的設想和最優的解決方案
第一次查詢,進入好友列表介面,查詢好友列表的會員資訊
從網路獲取資料:1使用者
從網路獲取資料:2使用者
第二次查詢,進入群聊介面,查詢群聊中的會員資訊
從本地獲取資料:1使用者
從網路獲取資料:3使用者
從網路獲取資料:4使用者
第三次查詢,定時更新最新會員資訊,更新所有快取裡的資料
從網路獲取資料:1使用者
從網路獲取資料:2使用者
從網路獲取資料:3使用者
從網路獲取資料:4使用者
複製程式碼