6章 RxJava基礎實戰

本篇文章已受權微信公衆號 YYGeeker 獨家發佈轉載請標明出處緩存

CSDN學院課程地址bash

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);
                }
            });
        }
    }
}
複製代碼

寫法二:app

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去查詢好友列表中會員信息,而後顯示會員圖標等信息。假如聊天列表界面中的好友數量有成千上百個,客戶端每次從後臺批量查詢用戶會員信息後,須要在本地作緩存,若是這時用戶在聊天列表中進入某個羣聊界面,這個時候仍是須要去獲取羣聊中的全部用戶的會員信息,若是羣聊界面中也包含有本身的好友,那麼咱們就會去判斷,若是用戶的會員信息在緩存中存在,則從緩存中獲取,若是在緩存中不存在,則加入到一個請求集合中,批量查詢會員信息後,合併本地緩存的會員信息和新的服務器的會員信息,將信息返回給羣聊界面ide

一、建立會員實體類工具

private HashMap<Long, Vip> mVipCache = new HashMap<>();//做爲緩存的類型

public static class Vip {
    //會員信息實體類
}
複製代碼

二、模擬從本地獲取數據源碼分析

  1. 遍歷批量的uid參數,從緩存和網絡中獲取會員信息的數據
  2. 若是本地數據存在,則須要將緩存的會員信息加入到vipInfo
  3. 若是本地數據不存在,則須要將uid加入到請求列表mRequestList
  4. 最後合併本地緩存數據和網絡請求數據
/**
 * 從本地緩存中獲取數據
 */
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));
}
複製代碼

三、模擬從服務器獲取數據ui

  1. 睡眠2s鍾,用於模擬網絡請求的耗時時間
  2. 模擬後臺返回的數據,並加入到緩存列表中
  3. 返回新的事件流
/**
 * 從網絡上批量查詢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);
}
複製代碼

四、模擬合併本地數據和服務器數據

  1. 模擬應用場景,查詢當前好友列表的會員信息
  2. 模擬應用場景,進入羣聊頁面,查詢羣聊列表的會員信息
  3. 模擬應用場景,定時更新會員的信息
/**
 * 模擬合併本地信息和服務器信息
 */
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);
}
複製代碼

五、輸出結果

達到咱們預期的設想和最優的解決方案

第一次查詢,進入好友列表界面,查詢好友列表的會員信息
從網絡獲取數據:1用戶
從網絡獲取數據:2用戶
第二次查詢,進入羣聊界面,查詢羣聊中的會員信息
從本地獲取數據:1用戶
從網絡獲取數據:3用戶
從網絡獲取數據:4用戶
第三次查詢,定時更新最新會員信息,更新全部緩存裏的數據
從網絡獲取數據:1用戶
從網絡獲取數據:2用戶
從網絡獲取數據:3用戶
從網絡獲取數據:4用戶
複製代碼
相關文章
相關標籤/搜索