
Sehr oft ist es notwendig, wiederholte Anfragen an das Netzwerk zu stellen, beispielsweise wenn der Benutzer nicht über das Internet verfügt und Daten aus dem Internet empfangen möchte. Es wäre schön, die Anfrage erneut zu werfen, wenn sie erscheint. Es wird empfohlen, dem Benutzer eine bestimmte Benutzeroberfläche anzuzeigen, die ihm erklärt, was passiert ist, und das erneute Auslösen der Anforderung zu ermöglichen. Das Hinzufügen einer solchen Logik kann sehr schmerzhaft sein, insbesondere wenn wir eine große Anzahl von ViewModel-Klassen haben. Natürlich können Sie die Neuabfragelogik in jeder ViewModel-Klasse implementieren, dies ist jedoch nicht praktisch und es besteht eine große Fehlerwahrscheinlichkeit.
Gibt es eine Möglichkeit, diese Verarbeitung nur einmal durchzuführen?
Glücklicherweise erlauben RxJava2 und Retrofit2 dies.
Es gibt bereits mehrere Lösungen für Stackoverflow:
1. Erstellen Sie Ihre eigene CallAdapterFactory (weitere Informationen
hier )
2. Wiederholen Sie die Kette mit PublishSubject (weitere Informationen
hier ).
Die erste Lösung verwendet RxJava1, ist bereits veraltet und wiederholt die Kette einfach mehrmals, ohne auf das Auftreten des Ereignisses zu reagieren. Die zweite Lösung ist gut, aber wir müssen den Operator retryWhen in jeder Kette verwenden. Und so habe ich die beiden Lösungen zu einer kombiniert.
Implementierung
Lassen Sie uns ein einfaches Projekt erstellen. Platzieren Sie zwei Registerkarten auf dem Hauptbildschirm. Jeder von ihnen zeigt Text an, der zeigt, wie viele Elemente von der API geladen werden. Wenn während der Ausführung ein Fehler auftritt, wird eine SnackBar mit der Schaltfläche "Erneut versuchen" angezeigt.

Wir definieren Basisklassen wie BaseActivity, BaseFragment, BaseViewModel. Sie sind erforderlich, um die Logik der Anforderungswiederholung an einer Stelle zu implementieren und Doppelarbeit dieses Codes zu vermeiden. Erstellen Sie zwei Fragmente, die BaseFragment erweitern. Jedes platzierte Fragment verfügt über ein eigenes ViewModel und stellt unabhängig Anforderungen an die API. Ich habe diese Snippets erstellt, um zu zeigen, dass bei einem Fehler jede Anforderung wiederholt wird. Erstellen Sie als Nächstes eine RxRetryCallAdapterFactory-Factory, die CallAdapter.Factory erweitert. Erstellen Sie anschließend eine Instanz von RxJava2CallAdapterFactory. Wir benötigen diese Instanz, um auf den RxJava2CallAdapter zuzugreifen, da wir den Code, der sich bereits in der Retrofit-Bibliothek befindet, nicht duplizieren möchten. Erstellen wir außerdem eine statische Methode, die eine Instanz unserer Factory zurückgibt. Beispielcode unten:
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) } }
Erstellen Sie als Nächstes einen RxRetryCallAdapter, der die CallAdapter-Schnittstelle implementiert, und wir müssen die CallAdapter-Instanz an den Konstruktor übergeben. Tatsächlich muss es sich um eine Instanz von RxJava2CallAdapter handeln, die die ursprüngliche Factory zurückgibt.
Als nächstes müssen wir die folgenden Dinge implementieren:
- retryWhen-Anweisung zum Implementieren der Wiederholungsfunktion
- doOnError () - Anweisung, die Fehler behandelt. Es wird zum Senden der Sendung verwendet, die in BaseActivity verarbeitet wird und dem Benutzer die SnackBar anzeigt.
- PublishSubject wird als Ereignisauslöser verwendet, der die Kette neu signiert.
- Der Operator ObservOn (Schedulers.io ()), der auf PublishSubject angewendet werden muss (wenn diese Zeile nicht hinzugefügt wird, erfolgt das Abonnement im Hauptthread und wir erhalten eine NetworkOnMainThreadException
- Wir transformieren PublishSubject in Flowable und setzen BackpressureStrategy.LATEST, da wir nur den letzten Fehler benötigen
Hinweis: Um PublishSubject bereitzustellen, habe ich eine einfache Singleton-Klasse erstellt, die alle Singleton-Abhängigkeiten im Projekt bereitstellt. In einem realen Projekt verwenden Sie wahrscheinlich ein Abhängigkeitsinjektionsframework wie 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()) } }
Wenn der Benutzer auf die Schaltfläche Erneut versuchen klickt, rufen wir onNext PublishSubject auf. Danach abonnieren wir die RX-Kette erneut.
Testen
Schalten Sie das Internet aus und führen Sie die Anwendung aus. Die Anzahl der geladenen Elemente ist auf jeder Registerkarte Null und SnackBar zeigt einen Fehler an. Schalten Sie das Internet ein und klicken Sie auf Try Adain. Nach einigen Sekunden ändert sich die Anzahl der geladenen Elemente auf jeder der Registerkarten.

Wenn jemand es braucht, dann ist der Quellcode
hier