
例如,当用户没有Internet并想从Internet接收数据时,经常需要对网络进行重复请求。 最好在请求出现时重新抛出它。 最好向用户显示一个特定的用户界面,该界面可以向他解释发生了什么并允许再次引发该请求。 添加这样的逻辑可能会很痛苦,尤其是当我们有大量的ViewModel类时。 当然,您可以在每个ViewModel类中实现重新查询逻辑,但这并不方便,而且出错的可能性很大。
有没有办法只执行一次此处理?
幸运的是,RxJava2和Retrofit2允许这样做。
Stackoverflow已经有几种解决方案:
1.创建自己的CallAdapterFactory(更多信息,请参见)
2.使用PublishSubject重复链(
此处有更多信息)
第一个解决方案使用RxJava1,它已经过时了,并且只重复执行了几次链,而不会对事件的发生做出反应。 第二种解决方案很好,但是我们需要在每个链中使用retryWhen运算符。 因此,我将两种解决方案合为一体。
实作
让我们创建一个简单的项目。 在主屏幕上放置两个选项卡。 它们每个都显示文本,这些文本将显示API加载了多少个元素。 如果在执行过程中发生错误,我们将显示一个SnackBar和Try Again按钮。

我们将诸如BaseActivity,BaseFragment,BaseViewModel之类的基类定义为在一个地方实现请求重复的逻辑并避免重复此代码所必需。 创建两个将扩展BaseFragment的片段。 每个放置的片段都有自己的ViewModel,并独立地向API发出请求。 我创建了这些片段,以表明如果发生错误,将重复每个请求。 接下来,创建一个扩展CallAdapter.Factory的RxRetryCallAdapterFactory工厂。 之后,创建一个RxJava2CallAdapterFactory实例。 我们需要此实例来访问RxJava2CallAdapter,因为我们不想复制Retrofit库中已经存在的代码。 另外,让我们创建一个静态方法,该方法将返回我们工厂的一个实例。 下面的示例代码:
class RxRetryCallAdapterFactory : CallAdapter.Factory() { companion object { fun create() : CallAdapter.Factory = RxRetryCallAdapterFactory() } private val originalFactory = RxJava2CallAdapterFactory.create() override fun get(returnType : Type, annotations : Array<Annotation>, retrofit : Retrofit) : CallAdapter<*, *>? { val adapter = originalFactory.get(returnType, annotations, retrofit) ?: return null return RxRetryCallAdapter(adapter) } }
接下来,创建一个实现CallAdapter接口的RxRetryCallAdapter,我们需要将CallAdapter实例传递给构造函数。 实际上,它必须是RxJava2CallAdapter的实例,该实例返回原始工厂。
接下来,我们需要实现以下内容:
- retryWhen语句用于实现重复功能
- 处理错误的doOnError()语句。 它用于发送广播,该广播在BaseActivity中处理并向用户显示SnackBar。
- PublishSubject用作重新签名链的事件触发器。
- 需要应用于PublishSubject的observeOn(Schedulers.io())运算符(如果未添加此行,则订阅将在主线程中发生,我们将获得NetworkOnMainThreadException
- 我们将PublishSubject转换为Flowable并设置BackpressureStrategy.LATEST,因为我们只需要最后一个错误
注意:为了提供PublishSubject,我创建了一个简单的单例类,该类提供了项目中的所有单例依赖项。 在实际的项目中,您可能会使用像Dagger2这样的依赖项注入框架
class RxRetryCallAdapter<R>(private val originalAdapter : CallAdapter<R, *>) : CallAdapter<R, Any> { override fun adapt(call : Call<R>) : Any { val adaptedValue = originalAdapter.adapt(call) return when (adaptedValue) { is Completable -> { adaptedValue.doOnError(this::sendBroadcast) .retryWhen { AppProvider.provideRetrySubject().toFlowable(BackpressureStrategy.LATEST) .observeOn(Schedulers.io()) } } is Single<*> -> { adaptedValue.doOnError(this::sendBroadcast) .retryWhen { AppProvider.provideRetrySubject().toFlowable(BackpressureStrategy.LATEST) .observeOn(Schedulers.io()) } }
当用户单击“重试”按钮时,我们将调用onNext PublishSubject。 之后,我们重新订阅rx链。
测试中
关闭Internet并运行该应用程序。 每个选项卡上已加载的项目数为零,并且SnackBar显示错误。 打开Internet,然后单击Try Adain。 几秒钟后,每个选项卡上加载项目的数量都会改变。

如果有人需要它,那么源代码就
在这里