
Muitas vezes, é necessário fazer solicitações repetidas à rede, por exemplo, quando o usuário não tinha a Internet e queria receber dados da Internet. Seria bom relançar a solicitação quando ela aparecer. É uma boa prática mostrar ao usuário uma interface do usuário específica que explique o que aconteceu e permita que a solicitação seja lançada novamente. Adicionar essa lógica pode ser bastante doloroso, especialmente quando temos um grande número de classes ViewModel. Obviamente, você pode implementar a lógica de consulta em cada classe do ViewModel, mas isso não é conveniente e há uma grande chance de erros.
Existe uma maneira de fazer esse processamento apenas uma vez?
Felizmente, RxJava2 e Retrofit2 permitem isso.
Já existem várias soluções no Stackoverflow:
1. Criando seu próprio CallAdapterFactory (mais informações
aqui )
2. Repita a cadeia usando o PublishSubject (mais informações
aqui )
A primeira solução usa RxJava1, já está desatualizada e também simplesmente repete a cadeia várias vezes, sem reagir à ocorrência do evento. A segunda solução é boa, mas precisamos usar o operador retryWhen em cada cadeia. E assim, eu combinei as duas soluções em uma.
Implementação
Vamos criar um projeto simples. Coloque duas guias na tela principal. Cada um deles exibe texto que mostra quantos elementos são carregados pela API. Se ocorrer um erro durante a execução, exibimos uma SnackBar com um botão Try Again.

Definimos classes base como BaseActivity, BaseFragment, BaseViewModel, elas são necessárias para implementar a lógica da repetição de solicitação em um único local e evitar a duplicação desse código. Crie dois fragmentos que estenderão BaseFragment. Cada fragmento colocado possui seu próprio ViewModel e faz solicitações de forma independente à API. Criei esses trechos para mostrar que, se ocorrer um erro, cada solicitação será repetida. Em seguida, crie uma fábrica RxRetryCallAdapterFactory que estenda o CallAdapter.Factory. Depois disso, crie uma instância de RxJava2CallAdapterFactory. Precisamos desta instância para acessar o RxJava2CallAdapter, pois não queremos duplicar o código que já está na biblioteca Retrofit. Além disso, vamos criar um método estático que retornará uma instância de nossa fábrica. Código de exemplo abaixo:
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) } }
Em seguida, crie um RxRetryCallAdapter que implemente a interface CallAdapter e precisamos passar a instância CallAdapter para o construtor. De fato, deve ser uma instância do RxJava2CallAdapter, que retorna a fábrica original.
Em seguida, precisamos implementar o seguinte:
- retryWhen instrução usada para implementar a funcionalidade de repetição
- Instrução doOnError () que lida com erros. É usado para enviar uma transmissão que é processada no BaseActivity e mostra a SnackBar para o usuário.
- PublishSubject é usado como um gatilho de evento que assina novamente a cadeia.
- O operador observeOn (Schedulers.io ()) que deve ser aplicado ao PublishSubject (se essa linha não for adicionada, a assinatura ocorrerá no thread principal e obteremos um NetworkOnMainThreadException
- Transformamos PublishSubject em Flowable e configuramos BackpressureStrategy.LATEST, pois precisamos apenas do último erro
Nota: Para fornecer PublishSubject, criei uma classe singleton simples que expõe todas as dependências singleton no projeto. Em um projeto real, é provável que você use uma estrutura de injeção de dependência como o 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()) } }
Quando o usuário clica no botão Tentar novamente, chamamos o Next PublishSubject. Depois disso, reinscrevemos a cadeia rx.
Teste
Desligue a Internet e execute o aplicativo. O número de itens carregados é zero em cada guia e o SnackBar exibe um erro. Ligue a Internet e clique em Try Adain. Após alguns segundos, o número de itens carregados muda em cada uma das guias.

Se alguém precisar, o código fonte está
aqui