[英]Retry a call with Retrofit 2 and RxJava2 after displaying a dialog
I'm calling an API using Retrofit 2 and RxJava2. 我正在使用Retrofit 2和RxJava2调用API。 If a call fails, in some cases (eg no Internet connection), I want to display an error dialog to the user and let him retry.
如果呼叫失败,在某些情况下(例如没有Internet连接),我想向用户显示一个错误对话框,让他重试。
As I'm using RxJava, I was thinking of using .retryWhen(...)
but I don't know how to do that as it needs to wait for the user to press the button on the dialog. 当我使用RxJava时,我正在考虑使用
.retryWhen(...)
但我不知道该怎么做,因为它需要等待用户按下对话框上的按钮。
At the moment I display the dialog but it retries before the user presses any button. 目前我显示对话框,但在用户按下任何按钮之前重试。 Plus I would like the call to not be retried when the user presses 'cancel'.
另外,当用户按下“取消”时,我希望不再重试该呼叫。
Here is the code I have at the moment: 这是我目前的代码:
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*/)
}));
When button is clicked you trigger this event: 单击按钮时,您将触发此事件:
retrySubject.onNext(new Object());
As you can see in this Marble diagram: 正如您在此Marble图中所看到的:
the error is not propagated. 错误不会传播。 The
retryWhen
operator will indeed handle it and do a proper action. retryWhen
运算符确实会处理它并执行适当的操作。 This is the reason why you have to enable (or for example show a Dialog) in doOnError
, before retryWhen
operator. 这就是为什么你必须在
retryWhen
运算符之前在doOnError
启用(或者例如显示一个Dialog)的doOnError
。
When you don't want to listen anymore for successive retry attempts, you just need to unsubscribe: 如果您不想再继续听重试,您只需要取消订阅:
disposable.dispose();
As per your question: 根据你的问题:
What should I do if I want to retry only on a specific Exception but not on the other ones?
如果我只想在特定的异常上重试而不在其他异常上重试,我该怎么办?
You can modify your retryWhen
in this way: 您可以通过以下方式修改您的重
retryWhen
:
.retryWhen(throwableObservable -> throwableObservable.flatMap(throwable -> {
if (throwable instanceof TargetException) {
return Observable.just(throwable).zipWith(retrySubject, (o, o2) -> o);
} else {
throw Throwables.propagate(throwable);
}
}))
Where Throwables.propagate(throwable)
is a Guava util that can be replaced with throw new RuntimeException(throwable);
其中
Throwables.propagate(throwable)
是一个Guava util,可以用throw new RuntimeException(throwable);
替换throw new RuntimeException(throwable);
for me i am using clean architecture and it was hard to put android code to show dialog in usecase or repository. 对我来说,我使用干净的架构,很难将android代码放在usecase或repository中显示对话框。 so since i was using kotlin i passed in a lamba to take care of things for me.
所以,因为我使用kotlin我通过lamba来为我照顾好事。 something like this:
这样的事情:
here is my full presenter: 这是我的全部主持人:
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() }
}
then in then errordialog i do this assuming its a dialogfragment: 那么在errordialog我这样做假设它是一个dialogfragment:
lateinit var retryAction: () -> Unit
override fun onStart() {
super.onStart()
dialog.window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
button.setOnClickListener { dismiss();retryAction() }
}
my code for on error is not acutally like that but just to give you an idea on how to pass in a lambda. 我的错误代码并非如此,只是为了让你了解如何传入lambda。 im sure you can pass in a function of your own.
我相信你可以通过自己的功能。 in this case the lambda was the method itself to retry.
在这种情况下,lambda是重试的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.