Throwing captured exception results in CompositeException - android

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

RxJava how to handle errors differently at different points in a chain

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.

How can RxJava2's onErrorResumeNext counterpart in Kotlin Flow that returns another flow be implemented?

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)) })
}

How to throw an exception using RxJava without OnNext

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.

RxJava's retryWhen operator

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

Searching with RxJava not working

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.

Categories

Resources