简体   繁体   English

显示对话框后,使用Retrofit 2和RxJava2重试呼叫

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM