本篇文章已授权微信公众号 YYGeeker
独家发布转载请标明出处
CSDN学院课程地址
- 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基础实战
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用户