Estou usando o rxjava em meu aplicativo Android para lidar com solicitações de rede de forma assíncrona. Agora, gostaria de repetir uma solicitação de rede com falha somente após um determinado período de tempo.
Existe alguma maneira de usar retry () em um Observable, mas tentar novamente apenas após um certo atraso?
Existe uma maneira de informar ao observável que está sendo tentado novamente (em vez de tentado pela primeira vez)?
Eu dei uma olhada em debounce () / throttleWithTimeout (), mas eles parecem estar fazendo algo diferente.
Editar:
Acho que encontrei uma maneira de fazer isso, mas estou interessado em confirmar se essa é a maneira correta de fazer isso ou em outras maneiras melhores.
O que estou fazendo é o seguinte: no método call () do meu Observable.OnSubscribe, antes de chamar o método Subscribers onError (), simplesmente deixo o Thread dormir pelo tempo desejado. Então, para tentar novamente a cada 1000 milissegundos, eu faço algo assim:
@Override
public void call(Subscriber<? super List<ProductNode>> subscriber) {
try {
Log.d(TAG, "trying to load all products with pid: " + pid);
subscriber.onNext(productClient.getProductNodesForParentId(pid));
subscriber.onCompleted();
} catch (Exception e) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e.printStackTrace();
}
subscriber.onError(e);
}
}
Como esse método está sendo executado em um thread de IO, ele não bloqueia a IU. O único problema que vejo é que até o primeiro erro é relatado com atraso, então o atraso existe mesmo se não houver nova tentativa (). Eu gostaria melhor se o atraso não fosse aplicado após um erro, mas antes de uma nova tentativa (mas não antes da primeira tentativa, obviamente).
Error:(73, 20) error: incompatible types: RetryWithDelay cannot be converted to Func1<? super Observable<? extends Throwable>,? extends Observable<?>>
RetryWithDelay
para este: pastebin.com/6SiZeKnCInspirado pela resposta de Paul , e se você não está preocupado com os
retryWhen
problemas declarados por Abhijit Sarkar , a maneira mais simples de atrasar a nova assinatura com rxJava2 incondicionalmente é:Você pode querer ver mais exemplos e explicações sobre retryWhen e repeatWhen .
fonte
Este exemplo funciona com jxjava 2.2.2:
Tentar novamente sem demora:
Tentar novamente com atraso:
Nossa fonte falha se someConnection.send () falhar. Quando isso acontece, o observável de falhas dentro de retryWhen emite o erro. Atrasamos essa emissão em 300 ms e a enviamos de volta para sinalizar uma nova tentativa. take (5) garante que nossa sinalização observável terminará após recebermos cinco erros. retryWhen vê o encerramento e não tenta novamente após a quinta falha.
fonte
Esta é uma solução baseada nos trechos de Ben Christensen que vi, RetryWhen Example e RetryWhenTestsConditional (tive que mudar
n.getThrowable()
paran
para que funcionasse). Usei evant / gradle-retrolambda para fazer a notação lambda funcionar no Android, mas você não precisa usar lambdas (embora seja altamente recomendado). Para o atraso, implementei o backoff exponencial, mas você pode inserir qualquer lógica de backoff que quiser. Para completar, adicionei os operadoressubscribeOn
eobserveOn
. Estou usando ReactiveX / RxAndroid para oAndroidSchedulers.mainThread()
.fonte
Observable
objetos?kjones
solução e está funcionando perfeitamente para mim, obrigadoem vez de usar MyRequestObservable.retry, uso uma função de invólucro retryObservable (MyRequestObservable, retrycount, segundos) que retorna um novo Observable que trata da indireção para o atraso para que eu possa fazer
fonte
retryWhen
é um operador complicado, talvez até com erros. O doc oficial e pelo menos uma resposta aqui usamrange
operador, que irá falhar se não houver novas tentativas a serem feitas. Veja minha discussão com o membro do ReactiveX David Karnok.Melhorei a resposta de kjones mudando
flatMap
paraconcatMap
e adicionando umaRetryDelayStrategy
classe.flatMap
não preserva a ordem de emissão enquanto oconcatMap
faz, o que é importante para atrasos com retirada. ORetryDelayStrategy
, como o nome indica, permite que o usuário escolha entre vários modos de geração de atrasos na nova tentativa, incluindo back-off. O código está disponível em meu GitHub completo com os seguintes casos de teste:Veja o
setRandomJokes
método.fonte
Agora, com o RxJava versão 1.0+, você pode usar zipWith para obter uma nova tentativa com atraso.
Adicionando modificações à resposta do kjones .
Modificado
fonte
Mesma resposta de kjones, mas atualizado para a versão mais recente Para a versão RxJava 2.x : ('io.reactivex.rxjava2: rxjava: 2.1.3')
Uso:
// Adicionar lógica de repetição ao observável existente. // Tente novamente no máximo 3 vezes com um atraso de 2 segundos.
fonte
Com base na resposta do kjones, aqui é a versão Kotlin do RxJava 2.x, tente novamente com um atraso como uma extensão. Substitua
Observable
para criar a mesma extensão paraFlowable
.Em seguida, basta usá-lo em observáveis
observable.retryWithDelay(3, 1000)
fonte
Single
também?flatMap
terá que usarFlowable.timer
eFlowable.error
mesmo que a função sejaSingle<T>.retryWithDelay
.Você pode adicionar um atraso no Observable retornado no operador retryWhen
Você pode ver mais exemplos aqui. https://github.com/politrons/reactive
fonte
Simplesmente faça assim:
fonte
Para a versão Kotlin e RxJava1
fonte
(Kotlin) Eu melhorei um pouco o código com backoff exponencial e defesa aplicada emitindo Observable.range ():
fonte
no caso de precisar imprimir a contagem de novas tentativas, você pode usar o exemplo fornecido na página wiki de Rxjava https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators
fonte