本文主要探究用Rxjava2 + Retrofit进行网络请求时, 应该在哪里调用 showLoading()
, 在哪里调用 hideLoading()
.
别告诉我, 你是这样调用的....
//网络请求获取用户信息
showLoading();
API.getUserInfo()
....
.subscribe((integer -> {
hideLoading();
//dosomething
...
}, e -> {
hideLoading();
//handle error
...
});
当然也无妨, 只不过丑了点... 这里, 我们用doXXX
操作符优雅的解决这个问题.
首先探究下各种 doXXX
操作符的执行时机: (类似于声明周期)
1. 正常执行
测试代码
Observable.create(emiter -> {
emiter.onNext(1);
emiter.onComplete();
})
.doOnSubscribe(d -> LOG.d(TAG, "doOnSubscribe"))
.doOnNext(integer -> LOG.d(TAG, "doOnNext"))
.doAfterNext(integer -> LOG.d(TAG, "doAfterNext"))
.doOnComplete(() -> LOG.d(TAG, "doOnComplete"))
.doOnError(throwable -> LOG.d(TAG, "doOnError"))
.doOnTerminate(() -> LOG.d(TAG, "doOnTerminate"))
.doAfterTerminate(() -> LOG.d(TAG, "doAfterTerminate"))
.doFinally(() -> LOG.d(TAG, "doFinally"))
.doOnDispose(() -> LOG.d(TAG, "doOnDispose"))
.subscribe(integer -> {
LOG.d(TAG, "onNext");
}, e -> {
LOG.d(TAG, "onError");
}, () -> {
LOG.d(TAG, "onComplete");
});
结果
// doOnSubscribe
// doOnNext
// onNext
// doAfterNext
// doOnComplete
// doOnTerminate
// onComplete
// doFinally
// doAfterTerminate
2. 上游抛异常
测试代码
Observable.create(emiter -> {
emiter.onError(new NullPointerException());
})
.doOnSubscribe(d -> LOG.d(TAG, "doOnSubscribe"))
.doOnNext(integer -> LOG.d(TAG, "doOnNext"))
.doAfterNext(integer -> LOG.d(TAG, "doAfterNext"))
.doOnComplete(() -> LOG.d(TAG, "doOnComplete"))
.doOnError(throwable -> LOG.d(TAG, "doOnError"))
.doOnTerminate(() -> LOG.d(TAG, "doOnTerminate"))
.doAfterTerminate(() -> LOG.d(TAG, "doAfterTerminate"))
.doFinally(() -> LOG.d(TAG, "doFinally"))
.doOnDispose(() -> LOG.d(TAG, "doOnDispose"))
.subscribe((integer -> {
LOG.d(TAG, "onNext");
}, e -> {
LOG.d(TAG, "onError");
}, () -> {
LOG.d(TAG, "onComplete");
}));
结果
// doOnSubscribe
// doOnError
// doOnTerminate
// onError
// doFinally
// doAfterTerminate
根据以上执行结果可以看出, 不管是正常执行还是上游抛出异常,
开始都会调用doOnSubscribe
,
结束都会调用doOnTerminate
,doFinally
,doAfterTerminate
.
那么是不是我们可以从以上三个方法中随便选一个作为结束的节点, 调用hideLoading()
就可以呢?
非也! 下面来看一个很多人容易忽略的问题, 在下游的 onNext()
方法中抛出异常,
3. 下游 onNext 抛异常
测试代码
Observable.create(emiter -> {
emiter.onNext(1);
emiter.onComplete();
})
.doOnSubscribe(d -> LOG.d(TAG, "doOnSubscribe"))
.doOnNext(integer -> LOG.d(TAG, "doOnNext"))
.doAfterNext(integer -> LOG.d(TAG, "doAfterNext"))
.doOnComplete(() -> LOG.d(TAG, "doOnComplete"))
.doOnError(throwable -> LOG.d(TAG, "doOnError"))
.doOnTerminate(() -> LOG.d(TAG, "doOnTerminate"))
.doAfterTerminate(() -> LOG.d(TAG, "doAfterTerminate"))
.doFinally(() -> LOG.d(TAG, "doFinally"))
.doOnDispose(() -> LOG.d(TAG, "doOnDispose"))
.subscribe(integer -> {
LOG.d(TAG, "onNext");
throw new NullPointerException(); //此处抛出异常
}, e -> {
LOG.d(TAG, "onError");
}, () -> {
LOG.d(TAG, "onComplete");
});
结果
// doOnSubscribe
// doOnNext
// onNext
// doOnDispose
// doFinally
// onError
// doAfterNext
在onNext()
中抛出异常就是你的业务逻辑中报错了, 此时会调用onError()
, 但不会调用doOnError
, doOnTerminate
和 doAfterTerminate
.
所以如果你选择了doOnTerminate
或者 doAfterTerminate
作为了你的结束节点, 那么就可能会出现loading显示后无法隐藏的问题..
综上, 我们找到了整个事件的首尾节点:
开始必然执行doOnSubscribe
结束必然执行doFinally
优雅的方式:
//网络请求获取用户信息
API.getUserInfo()
.doOnSubscribe(d -> showLoading())
.doFinally(() -> hideLoading())
.subscribe((integer -> {
//dosomething
}, e -> {
//handle error
});