目录
9.网络请求中发现token过期后刷新token并重新发起请求
(2)定义任务Fragment:WorkerFragment
(3)模拟一个旋转屏幕需要销毁重建的Activity(实现Iholder接口)
1.计算一段时间内的平均值
应用场景:不定期产生一些数值,需要计算固定一段时间内的平均值
(1)Observable 模拟了按随机时间发送数据值的场景。
(2)buffer(200,TimeUnit.MILLISECONDS) 把一段时间(200s)发送的数据值收集起来,生成一个List发送给Consumer,Consumer收到后计算出平均值。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Throwable {
e.onNext(1);
Thread.sleep(50);
e.onNext(2);
Thread.sleep(90);
e.onNext(3);
Thread.sleep(60);
e.onNext(4);
Thread.sleep(70);
}
}).buffer(200,TimeUnit.MILLISECONDS)
.subscribe(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> integers) throws Throwable {
if(integers.size() <= 0) {
return;
}
int result = 0;
for(Integer i : integers) {
result += i;
}
Log.i("avg","avg = " + result / integers.size());
}
});
2.优化搜索联想功能
搜索联想:每当搜索框中输入的key发生变化,随即返回相应的搜索结果并展示。
优化项:
(1)连续输入引发的不必要请求。用户输入abc,App会连续发起三次关键字搜索request:a,ab,abc
(2)连续request,返回乱序。(1)场景下无法保证response按照a,ab,abc顺序返回,导致实际搜索结果错误。
解决方案:
按照上述场景,实际需要请求的是abc,即输入的最后结果key!
(1)debounce操作符,当输入框发生变化时,不会立刻将事件发送给下游,而是等待500ms,如果在这段事件内,输入框没有发生变化,那么才发送该事件;反之,则在收到新的关键词后,继续等待500ms。
(2)filter操作符,只有关键词的长度大于0时才发送事件给Observer。
(3)switchMap操作符,即便当ab、abc都触发request,即使ab的结果返回了,也不会发送给Observer,从而避免了出现前面介绍的搜索词和联想结果不匹配的问题。
RxTextView.textChanges(editText)
// 表示延时多少秒后执行,当你敲完字之后停下来的半秒就会执行下面语句
.debounce(500, TimeUnit.MILLISECONDS)
// 只有关键词的长度大于0时才发送事件给Observer
.filter(new Predicate<CharSequence>() {
@Override
public boolean test(CharSequence charSequence) throws Exception {
return charSequence != null && charSequence.length() > 0;
}
})
// 数据转换 flatMap: 当同时多个数据请求访问的时候,前面的网络数据会覆盖后面的网络数据
// 数据转换 switchMap: 当同时多个网络请求访问的时候,会以最后一个发送请求为准,前面网络数据会被最后一个覆盖
.switchMap(new Function<CharSequence, ObservableSource<List<String>>>() {
@Override
public ObservableSource<List<String>> apply(
@NonNull CharSequence charSequence) throws Exception {
// 网络请求操作,获取我们需要的数据
List<String> list = new ArrayList<String>();
list.add("2017");
list.add("2018");
return Observable.just(list);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
3.优化多次重复点击
场景:用户多次点击,造成重复请求。
RxView.clicks(button).throttleFirst(1, TimeUnit.SECONDS)
.subscribe(new Observer<Object>() {
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(Object o) {
Log.i("JAVA", "onClick");
}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
4.RxJava + Retrofit + OkHttp
参见Retrofit相关资料
5.页面轮询请求
interval操作符:固定延时请求,每隔n秒请求一次。
Observable
// 每间隔10s轮询一次
.interval(10, TimeUnit.SECONDS)
// 防止由于Activity已经销毁,但Observable依然在发送数据导致的内存泄漏
.takeWhile(new Predicate<Long>() {
@Override
public boolean test(Long aLong) throws Exception {
// 用于中断interval的发送,当Activity在 onDestory / onPause等方法 设置 needRefresh 为 false时,Observable将停止发送数据
return needReFresh;
}
}).subscribeOn(Schedulers.computation())
.observeOn(Schedulers.io())
.map(new Function<Long, String>() {
@Override
public String apply(Long aLong) throws Exception {
// 发送网络请求,获取结果
return null;
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(String string) {
// update UI
}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
扩展:也可以用repeat,repeatWhen实现。
6.接口重新请求(retry)
应用场景:
1.规定重试次数
2.规定重试条件,什么条件下需要重试,什么条件下不需要重试,重试的时间、方式等
解决方案:
retryWhen + flatMap
// 用于在UI线程结束前,清空订阅对象,防止内存溢出
final CompositeDisposable mCompositeDisposable = new CompositeDisposable();
final String ERROR_CODE1 = "error_code1";
final String ERROR_CODE2 = "error_code2";
final int apiMaxRetryCount = 4;
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(@NonNull ObservableEmitter<String> e) throws Throwable {
// doNetWork
String result = doNetWork();
// 模拟失败
if (null == result) {
e.onError(new Throwable(ERROR_CODE1));
} else {
// 模拟成功
e.onNext(result);
e.onComplete();
}
}
}).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
private int retryCount = -1;
private long retryWaitingTime = 0;
@Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Throwable {
// 根据Throwable类型,实现不同的重试策略(是否重试,重试延时,重试次数)
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(Throwable throwable) throws Throwable {
// throwable为空属于异常情况,不做重试,直接结束
if (null == throwable) {
return Observable.error((throwable));
}
switch (throwable.getMessage()) {
case ERROR_CODE1:
// ERROR_CODE1,延时1000s后重试
retryWaitingTime = 1000;
break;
case ERROR_CODE2:
// ERROR_CODE2,延时2000s后重试
retryWaitingTime = 2000;
break;
default:
break;
}
retryCount++;
// 控制重试次数小于允许的最大重试次数;否则返回error,调用Observer的onError,结束本次请求
return retryWaitingTime > -1 && retryCount < apiMaxRetryCount ?
Observable.timer(retryWaitingTime, TimeUnit.SECONDS)
: Observable.error(throwable);
}
});
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
mCompositeDisposable.add(d);
}
@Override
public void onNext(@NonNull String s) {
// 接收正常请求结果,更新UI
}
@Override
public void onError(@NonNull Throwable e) {
// 处理异常情况
}
@Override
public void onComplete() {}
});
7.本地验证输入有效性
模拟场景:用户名、密码登录。长度要求分别为 2-8 和 4-16。如果两者都正确,“登录”恢复可点击,否则置灰不可点击
final CompositeDisposable compositeDisposable = new CompositeDisposable();
PublishSubject<String> userNamePublisher = PublishSubject.create();
PublishSubject<String> userPasswordPublisher = PublishSubject.create();
userNameEdit.addTextChangedListener(new EditTextMonitor(userNamePublisher));
passwordEdit.addTextChangedListener(new EditTextMonitor(userPasswordPublisher));
Observable.combineLatest(userNamePublisher, userPasswordPublisher, new BiFunction<String, String, Boolean>() {
@Override
public Boolean apply(String name, String password) {
if (null == name || null == password) {
return false;
}
return name.length() >= 2 && name.length() <= 8 && password.length() >= 4 && password.length() <= 16;
}
}).subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
compositeDisposable.add(d);
}
@Override
public void onNext(@NonNull Boolean aBoolean) {
// update UI
}
@Override
public void onError(@NonNull Throwable e) { }
@Override
public void onComplete() { }
});
首先创建两个PublishSubject,用于对用户名、密码输入框的订阅,通过重写TextWatcher,实现在afterTextChanged(Editable editable) 触发时,使用Pub