在显示对话框后重试使用 Retrofit 2 和 RxJava2 的调用
Retry a call with Retrofit 2 and RxJava2 after displaying a dialog
我正在使用 Retrofit 2 和 RxJava2 调用 API。
如果调用失败,在某些情况下(例如没有互联网连接),我想向用户显示一个错误对话框并让他重试。
因为我正在使用 RxJava,所以我正在考虑使用 .retryWhen(...)
但我不知道该怎么做,因为它需要等待用户按下对话框上的按钮。
目前我显示对话框,但它会在用户按下任何按钮之前重试。另外,我希望在用户按下 'cancel'.
时不重试呼叫
这是我目前的代码:
private void displayDialog(DialogInterface.OnClickListener positive, DialogInterface.OnClickListener negative) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setMessage("Unexpected error, do you want to retry?")
.setPositiveButton("Retry", positive)
.setNegativeButton("Cancel", negative)
.show();
}
private Observable<Boolean> notifyUser() {
final PublishSubject<Boolean> subject = PublishSubject.create();
displayDialog(
(dialogInterface, i) -> subject.onNext(true),
(dialogInterface, i) -> subject.onNext(false)
);
return subject;
}
private void onClick() {
Log.d(TAG, "onClick");
getData()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.retryWhen(attempts -> {
return attempts.zipWith(
notifyUser(),
(throwable, res) -> res);
})
.subscribe(
s -> {
Log.d(TAG, "success");
});
}
final PublishSubject<Object> retrySubject = PublishSubject.create();
disposable.add(getData()
.doOnError(throwable -> enableButton())
.retryWhen(observable -> observable.zipWith(retrySubject, (o, o2) -> o))
.subscribeWith(/* do what you want with the result*/)
}));
单击按钮时触发此事件:
retrySubject.onNext(new Object());
正如您在此 Marble 图中所见:
错误未传播。 retryWhen
运算符确实会处理它并执行适当的操作。这就是为什么您必须在 doOnError
中启用(或例如显示对话框)在 retryWhen
运算符之前。
当您不想再收听连续的重试尝试时,您只需退订:
disposable.dispose();
根据你的问题:
What should I do if I want to retry only on a specific Exception but
not on the other ones?
您可以这样修改您的retryWhen
:
.retryWhen(throwableObservable -> throwableObservable.flatMap(throwable -> {
if (throwable instanceof TargetException) {
return Observable.just(throwable).zipWith(retrySubject, (o, o2) -> o);
} else {
throw Throwables.propagate(throwable);
}
}))
其中 Throwables.propagate(throwable)
是一个 Guava util,可以替换为 throw new RuntimeException(throwable);
对我来说,我使用的是干净的架构,很难放置 android 代码来在用例或存储库中显示对话框。所以自从我使用 kotlin 以来,我传递了一个 lamba 来为我处理事情。像这样:
这是我的完整演示者:
class WeatherDetailsPresenter @Inject constructor(var getCurrentWeatherWithForecastUsecase: GetCurrentWithForcastedWeatherUsecase) : BaseMvpPresenter<WeatherDetailsView>() {
fun presentCurrentAndForcastedWeather(country: String?) {
getCurrentWeatherWithForecastUsecase.takeUnless { country?.isBlank() == true }?.apply {
this.countyName = country
execute(object : DefaultSubscriber<Pair<CurrentWeatherModel, ForecastedWeatherModel>>() {
override fun onSubscribe(d: Disposable) {
super.onSubscribe(d)
showLoading(true)
}
override fun onNext(t: Pair<CurrentWeatherModel, ForecastedWeatherModel>) {
super.onNext(t)
view?.showCurrentWeather(t.first)
view?.showForcastWeather(t.second)
}
override fun onError(e: Throwable) {
super.onError(e)
//if i get an error pass in lambda to the dialog
myErrorDialog.setretryAction( {this@WeatherDetailsPresenter.presentCurrentAndForcastedWeather(country)})
}
override fun onComplete() {
super.onComplete()
showLoading(false)
}
})
} ?: run { view?.showCountryUnavailable() }
}
然后在 errordialog 中假设它是一个对话片段:
lateinit var retryAction: () -> Unit
override fun onStart() {
super.onStart()
dialog.window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
button.setOnClickListener { dismiss();retryAction() }
}
我的 on error 代码实际上并不像那样,只是为了让您了解如何传递 lambda。我确定您可以传递自己的功能。在这种情况下,lambda 是重试的方法本身。
我正在使用 Retrofit 2 和 RxJava2 调用 API。 如果调用失败,在某些情况下(例如没有互联网连接),我想向用户显示一个错误对话框并让他重试。
因为我正在使用 RxJava,所以我正在考虑使用 .retryWhen(...)
但我不知道该怎么做,因为它需要等待用户按下对话框上的按钮。
目前我显示对话框,但它会在用户按下任何按钮之前重试。另外,我希望在用户按下 'cancel'.
时不重试呼叫这是我目前的代码:
private void displayDialog(DialogInterface.OnClickListener positive, DialogInterface.OnClickListener negative) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setMessage("Unexpected error, do you want to retry?")
.setPositiveButton("Retry", positive)
.setNegativeButton("Cancel", negative)
.show();
}
private Observable<Boolean> notifyUser() {
final PublishSubject<Boolean> subject = PublishSubject.create();
displayDialog(
(dialogInterface, i) -> subject.onNext(true),
(dialogInterface, i) -> subject.onNext(false)
);
return subject;
}
private void onClick() {
Log.d(TAG, "onClick");
getData()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.retryWhen(attempts -> {
return attempts.zipWith(
notifyUser(),
(throwable, res) -> res);
})
.subscribe(
s -> {
Log.d(TAG, "success");
});
}
final PublishSubject<Object> retrySubject = PublishSubject.create();
disposable.add(getData()
.doOnError(throwable -> enableButton())
.retryWhen(observable -> observable.zipWith(retrySubject, (o, o2) -> o))
.subscribeWith(/* do what you want with the result*/)
}));
单击按钮时触发此事件:
retrySubject.onNext(new Object());
正如您在此 Marble 图中所见:
错误未传播。 retryWhen
运算符确实会处理它并执行适当的操作。这就是为什么您必须在 doOnError
中启用(或例如显示对话框)在 retryWhen
运算符之前。
当您不想再收听连续的重试尝试时,您只需退订:
disposable.dispose();
根据你的问题:
What should I do if I want to retry only on a specific Exception but not on the other ones?
您可以这样修改您的retryWhen
:
.retryWhen(throwableObservable -> throwableObservable.flatMap(throwable -> {
if (throwable instanceof TargetException) {
return Observable.just(throwable).zipWith(retrySubject, (o, o2) -> o);
} else {
throw Throwables.propagate(throwable);
}
}))
其中 Throwables.propagate(throwable)
是一个 Guava util,可以替换为 throw new RuntimeException(throwable);
对我来说,我使用的是干净的架构,很难放置 android 代码来在用例或存储库中显示对话框。所以自从我使用 kotlin 以来,我传递了一个 lamba 来为我处理事情。像这样:
这是我的完整演示者:
class WeatherDetailsPresenter @Inject constructor(var getCurrentWeatherWithForecastUsecase: GetCurrentWithForcastedWeatherUsecase) : BaseMvpPresenter<WeatherDetailsView>() {
fun presentCurrentAndForcastedWeather(country: String?) {
getCurrentWeatherWithForecastUsecase.takeUnless { country?.isBlank() == true }?.apply {
this.countyName = country
execute(object : DefaultSubscriber<Pair<CurrentWeatherModel, ForecastedWeatherModel>>() {
override fun onSubscribe(d: Disposable) {
super.onSubscribe(d)
showLoading(true)
}
override fun onNext(t: Pair<CurrentWeatherModel, ForecastedWeatherModel>) {
super.onNext(t)
view?.showCurrentWeather(t.first)
view?.showForcastWeather(t.second)
}
override fun onError(e: Throwable) {
super.onError(e)
//if i get an error pass in lambda to the dialog
myErrorDialog.setretryAction( {this@WeatherDetailsPresenter.presentCurrentAndForcastedWeather(country)})
}
override fun onComplete() {
super.onComplete()
showLoading(false)
}
})
} ?: run { view?.showCountryUnavailable() }
}
然后在 errordialog 中假设它是一个对话片段:
lateinit var retryAction: () -> Unit
override fun onStart() {
super.onStart()
dialog.window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
button.setOnClickListener { dismiss();retryAction() }
}
我的 on error 代码实际上并不像那样,只是为了让您了解如何传递 lambda。我确定您可以传递自己的功能。在这种情况下,lambda 是重试的方法本身。