7

我正在使用 Retrofit 2 和 RxJava2 调用 API。如果呼叫失败,在某些情况下(例如没有 Internet 连接),我想向用户显示一个错误对话框并让他重试。

当我使用 RxJava 时,我正在考虑使用,.retryWhen(...)但我不知道该怎么做,因为它需要等待用户按下对话框上的按钮。

目前我显示对话框,但它在用户按下任何按钮之前重试。另外,我希望在用户按下“取消”时不会重试呼叫。

这是我目前拥有的代码:

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");
                    });
}
4

2 回答 2

10
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, beforeretryWhen运算符中启用(或例如显示对话框)的原因。

当您不想再收听连续的重试尝试时,您只需要取消订阅:

disposable.dispose();

根据你的问题:

如果我只想重试特定异常而不重试其他异常,我该怎么办?

您可以通过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)可以替换的番石榴实用程序在哪里throw new RuntimeException(throwable);

于 2017-12-06T14:59:59.783 回答
0

对我来说,我使用的是干净的架构,很难将 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() }
    }

我的错误代码并不是那样的,只是为了让您了解如何传入 lambda。我确定你可以传入你自己的函数。在这种情况下,lambda 是重试的方法本身。

于 2018-07-28T12:36:21.057 回答