In the following code, I get an EmptyResultSetException when no more records are read from the Sqlite database. This is expected. This exception is caught in doOnError. If I throw this exception, the exception caught in the subscriber's onError handler becomes a CompositeException. The CompositeException contains a list of exceptions and in this case it only contains a single exception which happens to be the EmptyResultSetException. Originally, before changing my code, the EmptyResultSetException was sent directly to the subscriber's onError handler. Now it gets placed into a CompositeException. What could be causing this?
val msgToSendPublisher = BehaviorSubject.createDefault(MessageToSend())
msgToSendPublisher
.flatMap { _ ->
App.context.repository.getMessageToSend().flatMapObservable { messageToSend -> Observable.just(messageToSend) }
}
.doOnError { error ->
throw error
}
.doOnNext {
msgToSendPublisher.onNext(it)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ messageToSend ->
},
{ ex ->
if (ex !is EmptyResultSetException) {
}
},
{
}
)
Related
I have a chain of API calls in RxJava, and when one fails I need to abort the chain and handle the error. But each failure needs to be handled differently. I tried this:
netRequestOne()
.onErrorResumeNext {
handleErrorOne()
Single.error(it)
}
.flatMap {
netRequestTwo()
}
.onErrorResumeNext {
handleErrorTwo()
Single.error(it)
}
// more flatMaps with requests...
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
// deal with success
},
{
// no-op
}
)
But if a request throws an exception, all of the subsequent onErrorResumeNext()'s are called, not just the one tied to that request.
You can achieve the requested behavior for example with this:
val requestOne = Single.just("Response 1")
//.doOnSuccess { throw RuntimeException("Fail second request") } // (1)
.doOnError {
println("handleErrorOne")
}
val requestTwo = Single.just(10)
//.doOnSuccess { throw RuntimeException("Fail second request") } // (2)
.doOnError {
println("handleErrorTwo")
}
requestOne
.flatMap { oneResult -> requestTwo.map { twoResult -> Pair(oneResult, twoResult) } }
.doOnSuccess { responses: Pair<String, Int> ->
println(responses)
}
.flatMap { Single.just("More flatMaps") }
.subscribe({}, {})
You can uncomment (1) and/or (2) to simulate a fail in the first or second request. In case, both requests end successfully, responses are combined and you do some other processing.
I want to implement offline-last approach with Flow, first try to fetch data from remote source if it fails, for instance Retrofit throwing network exception, i want to fetch data from local source with the code below
return flow { emit(repository.fetchEntitiesFromRemote()) }
.map {
println("🍏 getPostFlowOfflineLast() First map in thread: ${Thread.currentThread().name}")
val data = if (it.isEmpty()) {
repository.getPostEntitiesFromLocal()
} else {
repository.deletePostEntities()
repository.savePostEntity(it)
repository.getPostEntitiesFromLocal()
}
entityToPostMapper.map(data)
}
.catch { cause ->
println("❌ getPostFlowOfflineLast() FIRST catch with error: $cause, in thread: ${Thread.currentThread().name}")
flow { emit(repository.getPostEntitiesFromLocal()) }
}
.map { postList ->
println("🎃 getPostFlowOfflineLast() Second map in thread: ${Thread.currentThread().name}")
ViewState<List<Post>>(
status = Status.SUCCESS,
data = postList
)
}
.catch { cause: Throwable ->
println("❌ getPostFlowOfflineLast() SECOND catch with error: $cause, in thread: ${Thread.currentThread().name}")
flow {
emit(
ViewState<List<Post>>(
Status.ERROR,
error = cause
)
)
}
}
But it gets stuck with exception
I: ❌ getPostFlowOfflineLast() FIRST catch with error: java.net.UnknownHostException: Unable to resolve host "jsonplaceholder.typicode.com": No address associated with hostname, in thread: main
What should be the right implementation to have any observable like with RxJava onResumeNext if repository function was an Observerable?
onErrorResumeNext { _: Throwable ->
Observable.just(repository.getPostEntitiesFromLocal())
}
Figured out that i can use emitAll with a flow to continue flow even multiple times.
.catch { cause ->
println("❌ getPostFlowOfflineLast() FIRST catch with error: $cause, in thread: ${Thread.currentThread().name}")
emitAll(flow { emit(repository.getPostEntitiesFromLocal()) })
}
.map {
if (!it.isNullOrEmpty()) {
entityToPostMapper.map(it)
} else {
throw EmptyDataException("No data is available!")
}
}
.map { postList ->
println("🎃 getPostFlowOfflineLast() Third map in thread: ${Thread.currentThread().name}")
ViewState(status = Status.SUCCESS, data = postList)
}
.catch { cause: Throwable ->
println("❌ getPostFlowOfflineLast() SECOND catch with error: $cause, in thread: ${Thread.currentThread().name}")
emitAll(flow { emit(ViewState(Status.ERROR, error = cause)) })
}
I'm trying to force throw an error during the fake downloading using RxJava:
disposable.add(fakeRepo.downloadSomething()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ a: String -> finished() },
{ b: Throwable? -> showError() }
))
fun downloadSomething(): Single<String> {
return Single.just("")
}
I found solutions only with onNext, but I don't want this in my code.
What I should do to invoke showError() ?
Currently I always get finished()
Just use Single.error:
http://reactivex.io/RxJava/javadoc/io/reactivex/Single.html#error-java.lang.Throwable-
public static Single error(Throwable exception)
Returns a Single that invokes a subscriber's onError method when the subscriber subscribes to it.
I'm trying to understand retryWhen operator in depth and I have some code as below.
Flowable.just(1, 2, 3, 4, 5)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retryWhen { throwable ->
Log.d("Debug", "retryWhen proceed...")
throw Exception("There is a exception")
}
.subscribe(
{ item ->
Log.d("Debug", "success : $item")
},
{ throwable ->
Log.d("Debug", "error : ${throwable.message}")
},
{
Log.d("Debug", "complete")
}
)
And the result is shwon as below.
Debug: retryWhen proceed...
Debug: error : There is a exception
The question is that when retryWhen operator is triggered?
I assume retryWhen operator will be triggered only when there is a exception occurs.
But the result is not what I thought obviously,
Any thoughts on this? Thanks!
retryWhen { errors -> ... } take an Observable<Throwable> and should return an Observable that return anything for retrying or an error for stop retrying.
One example could be:
.retryWhen(attempts -> {
return attempts.zipWith(Observable.range(1, 3), (n, i) -> i).flatMap(i -> {
System.out.println("delay retry by " + i + " second(s)");
return Observable.timer(i, TimeUnit.SECONDS);
});
})
(taken from http://reactivex.io/documentation/operators/retry.html)
This code will delay each retry.
By the way, throwing an exception is not the thing to do in this method.
Documentation:
* Great blog article that explained the retryWhen
I'm trying to do a simple search UI, where the text change triggers a search in the service and that gets mapped to a ViewState. It would seem easy, but the following code doesn't work:
queryText.filter { it.length > 3 }
.switchMap { service.search(it) }
.onErrorReturn { SearchResponse(null, it.message) }
.map { SearchViewState(items = it.items, error = it.error) }
.startWith { SearchViewState(loading = true) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { viewState.onNext(it) }
I've no idea what I did wrong, but through debugging I can see that the stream throws a NetworkOnMainThreadException and then terminates so new events are no longer processed.
What is the correct way to do this?
I assume queryText is the source of textchanges which happen on the main thread. Therefore subscribeOn has no effect on it. You should apply subscribeOn to the actual network call:
queryText.filter { it.length > 3 }
.switchMap {
service.search(it)
.subscribeOn(Schedulers.io())
.onErrorReturn { SearchResponse(null, it.message) }
.map { SearchViewState(items = it.items, error = it.error) }
.startWith ( SearchViewState(loading = true) )
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe { viewState.onNext(it) }
In addition, I think you have to do the error recovery and state changes associated with the particular network call, otherwise a failure will stop the entire sequence.