
Très souvent, il est nécessaire de faire des demandes répétées au réseau, par exemple, lorsque l'utilisateur ne dispose pas d'Internet et souhaite recevoir des données d'Internet. Ce serait bien de relancer la demande lorsqu'elle apparaîtra. Il est recommandé de montrer à l'utilisateur une interface utilisateur spécifique qui lui expliquerait ce qui s'est passé et permettrait de renvoyer la demande. L'ajout d'une telle logique peut être assez pénible, surtout lorsque nous avons un grand nombre de classes ViewModel. Bien sûr, vous pouvez implémenter la logique de nouvelle requête dans chaque classe ViewModel, mais ce n'est pas pratique et il existe un énorme risque d'erreurs.
Existe-t-il un moyen d'effectuer ce traitement une seule fois?
Heureusement, RxJava2 et Retrofit2 le permettent.
Il existe déjà plusieurs solutions sur Stackoverflow:
1. Création de votre propre CallAdapterFactory (plus d'informations
ici )
2. Répétez la chaîne en utilisant PublishSubject (plus d'informations
ici )
La première solution utilise RxJava1, elle est déjà dépassée et elle répète simplement la chaîne plusieurs fois, sans réagir à l'occurrence de l'événement. La deuxième solution est bonne, mais nous devons utiliser l'opérateur retryWhen dans chaque chaîne. Et donc, j'ai combiné les deux solutions en une seule.
Implémentation
Créons un projet simple. Placez deux onglets sur l'écran principal. Chacun d'eux affiche du texte qui montrera combien d'éléments sont chargés par l'API. Si une erreur se produit pendant l'exécution, nous affichons un SnackBar avec un bouton Try Again.

Nous définissons des classes de base telles que BaseActivity, BaseFragment, BaseViewModel, elles sont nécessaires pour implémenter la logique de répétition des demandes en un seul endroit et éviter la duplication de ce code. Créez deux fragments qui étendent BaseFragment. Chaque fragment placé a son propre ViewModel et fait indépendamment des requêtes à l'API. J'ai créé ces extraits pour montrer que si une erreur se produit, chaque demande sera répétée. Ensuite, créez une fabrique RxRetryCallAdapterFactory qui étend CallAdapter.Factory. Après cela, créez une instance de RxJava2CallAdapterFactory. Nous avons besoin de cette instance pour accéder à RxJava2CallAdapter, car nous ne voulons pas dupliquer le code qui se trouve déjà dans la bibliothèque Retrofit. Créons également une méthode statique qui retournera une instance de notre usine. Exemple de code ci-dessous:
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) } }
Ensuite, créez un RxRetryCallAdapter qui implémente l'interface CallAdapter et nous devons passer l'instance CallAdapter au constructeur. En fait, il doit s'agir d'une instance de RxJava2CallAdapter, qui renvoie la fabrique d'origine.
Ensuite, nous devons implémenter les choses suivantes:
- Instruction retryWhen utilisée pour implémenter la fonctionnalité de répétition
- Instruction doOnError () qui gère les erreurs. Il est utilisé pour envoyer la diffusion, qui est traitée dans BaseActivity et montre le SnackBar à l'utilisateur.
- PublishSubject est utilisé comme déclencheur d'événement qui re-signe la chaîne.
- Opérateur observeOn (Schedulers.io ()) qui doit être appliqué à PublishSubject (si cette ligne n'est pas ajoutée, l'abonnement se produira dans le thread principal et nous obtiendrons une exception NetworkOnMainThreadException
- Nous transformons PublishSubject en Flowable et définissons BackpressureStrategy.LATEST, car nous n'avons besoin que de la dernière erreur
Remarque: Pour fournir PublishSubject, j'ai créé une classe singleton simple qui expose toutes les dépendances singleton dans le projet. Dans un projet réel, vous êtes susceptible d'utiliser un framework d'injection de dépendances comme 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()) } }
Lorsque l'utilisateur clique sur le bouton Réessayer, nous appelons onNext PublishSubject. Après cela, nous nous réinscrivons à la chaîne rx.
Test
Désactivez Internet et exécutez l'application. Le nombre d'éléments chargés est nul sur chaque onglet et SnackBar affiche une erreur. Allumez Internet et cliquez sur Try Adain. Après quelques secondes, le nombre d'éléments chargés change sur chacun des onglets.

Si quelqu'un en a besoin, le code source est
ici