I'm using RxJava with a retrofit to make API calls,
By using RxJava methods like flatMap and map I'm making API calls as well as performing DB operations in room database on the background thread.
My implementation is perfect and working fine if there is no error, but In the case when I got an error or any exception while performing DB Operation, Application getting crashed due to following Rx error.
E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-1
The exception was not handled due to missing onError handler in the subscribe() method call.
I have used RxJava to perform my operation as below :
mDataManager.login(params)
.flatMap { loginResponse: LoginResponse ->
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap { object: SomeDataModel ->
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
// here making another API call based on previos API
return#flatMap mDataManager...
}.map {
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
return#map true
}
.observeOn(mSchedulerProvider.ui())
.subscribeOn(mSchedulerProvider.io())
.subscribe({ result ->
// updating view
}, { throwable ->
throwable.printStackTrace()
})
I'm getting an exception while inserting data to DB
Exception java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase:
But the error not handled and Application getting crash.
The error says that missing onError handler in the subscribe() method but in my code, I already override throwable to handle exception/errors.
Can anyone find, what I'm missing or what mistake I have done in code.
UPDATE
Found the solution, Mistake was here :
mDataManager.insertDataToDB(object).subscribe()
In the Above statement, I'm subscribing but was not handling the error for that and because of that error was not handled by rxJava and in the result, the application gets crashed.
Final Code as below :
mDataManager.login(params)
.flatMap { loginResponse: LoginResponse ->
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap { object: SomeDataModel ->
// avoid this inner subscribe
// mDataManager.insertDataToDB(object).subscribe()
return#flatMap mDataManager.insertDataToDB(object)
}
.flatMap {
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap {
// avoid this inner subscribe
//mDataManager.insertDataToDB(object).subscribe()
return#flatMap mDataManager.insertDataToDB(object)
}
.observeOn(mSchedulerProvider.ui())
.subscribeOn(mSchedulerProvider.io())
.subscribe({ result ->
// updating view
}, { throwable ->
throwable.printStackTrace()
})
Above code is working Fine!
The reason is you are subscribing here
.map {
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
return#map true
}
And this subscribe is not handling the error scenario.
I feel it's not a good practice to call subscribe() for the inner streams. In your scenario the flow is broken in-between.
The best way according to me is instead of using map and calling subscribe() here, use,
flatMap{
object -> mDataManager.insertDataToDB(object)
}
This way, if any error, it will be caught in last outer subscribe().
Hope this answer helps.
Related
So, I am getting an error in my Android app (Kotlin) when trying to subscribe to a PublishSubject.
The error explanation is pretty straight forward, however, I have failed trying to implement this, onError function and I am not sure how to do it in a god way.
Here the error
The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | com.androidnetworking.error.ANError
Here the PublishSubject:
var positionSubject = PublishSubject.create<Location>()
Here when I subscribe (which gives error inside the code of the subscription):
compositeDisposable.add(
positionSubject.subscribe {
// do some actions here that causes Exception
}
)
Here my attempt to fix it in a "nice" way (did not work, still crashes in subscribe):
compositeDisposable.add(
positionSubject
.onErrorReturn { t ->
Log.d("debug", "EXCEPTION OCCURRED")
Location("")}
.subscribe {
// do some actions here that causes Exception
}
)
Here what I ended up doing to fix it and not crashing:
compositeDisposable.add(
positionSubject.subscribe {
try{
// do some actions here that causes Exception
}catch(e:Exception){
Log.d("debug", "EXCEPTION OCCURRED $e")
}
}
)
I am wondering how to this in a cleaner way than using the try/catch block inside the subscribe, if it is even possible.
Following code is kotlin way to subscribe a PublishSubject
var positionSubject = PublishSubject.create<Location>()
positionSubject.subscribe({ location ->
}, { error ->
})
This should work fine.
I've trying https request on retrofit with RxJava
When network is not available, I want to handle like this.
1, check network status.
2, if network is available, retry request
3, if network is not available, no retry
-> after that, listen network status and when it will be back, then retry request
I think we should use retryWhen() operator but I don't know hot to do this
need help to have good solution
You should use retryWhen operator like you said.
http://reactivex.io/documentation/operators/retry.html
See under retryWhen section of RxKotlin.
RetryWhen operator "resubscribes" when inner observable emits an item (Observable's onNext or Single's onSuccess is called) or just does not retry and pass the throwable downstream when onError I called.
Above is my wording; the exact wording from the doc is as follows:
The retryWhen operator is similar to retry but decides whether or not
to resubscribe to and mirror the source Observable by passing the
Throwable from the onError notification to a function that generates a
second Observable, and observes its result to determine what to do. If
that result is an emitted item, retryWhen resubscribes to and mirrors
the source and the process repeats; if that result is an onError
notification, retryWhen passes this notification on to its observers
and terminates.
Suppose you have the following retrofit interface.
interface Api {
#GET("/api")
fun request(): Single<String>
}
In the retry block, you get a flowable of throwable (which will be mostly HttpException thrown from your retrofit interface), You should use flatMap operator on this flowable because you have to pass the throwable downstream when network is still not available.
ApiClient.instance.request()
.retryWhen { flowable: Flowable<Throwable> ->
flowable.flatMap { throwable ->
// check network status here
if (available) return#flatMap Flowable.just<Boolean>(true)
return#flatMap Flowable.error<Boolean>(throwable)
}
}
.subscribe({ response -> /* deal with success */}, { error -> /* deal with error */})
Watch out here that you have to match the type of retrying case and throwing case (Flowable<Boolean> in this case). It usually doesn't matter which type use choose as long as you emit an item when you want to retry and an error when you don't want to.
Handling network errors with rxJava and Retrofit is very easy as it just throws a RetrofitError in the onError method:
#Override
public void onError(Throwable e) {
if (e instanceof RetrofitError) {
if (((RetrofitError) e).isNetworkError()) {
//handle network error
} else {
//handle error message from server
}
}
}
I have a simple Android application with Room database and I am trying to react to an #Insert query with RxJava but I am unable to chain the calls correctly.
This is my view model method calling the insert:
fun insertTopic(): Single<Long> {
val topic = Topic(null, topicText.value!!, difficulty.value!!, false)
return Single.create<Long> { Observable.just(topicDao.insert(topic)) }
}
And this is the code in my activity triggering the save action:
disposable.add(RxView.clicks(button_save)
.flatMapSingle {
viewModel.insertTopic()
.subscribeOn(Schedulers.io())
}.observeOn(AndroidSchedulers.mainThread())
.doOnError { Toast.makeText(this, "Error inserting topic", Toast.LENGTH_SHORT).show() }
.subscribe { id ->
// NOT INVOKED
hideKeyboard()
Toast.makeText(this, "Topic inserted. ID: $id", Toast.LENGTH_SHORT).show()
this.finish
})
When I click the button, the entity is saved but none of the subscribe code is invoked (no toast is shown). Could someone point out to me what am I doing wrong? I am fairly new to RX java.
The problem is in incorrect usage of Single.create. There is no need in wrapping topicDao.insert(topic) into Observable. Moreover, you are implementing new Single manually, which means you must pass the result id to the #NonNull SingleEmitter<T> emitter argument. But there is no need in using Single.create here.
Single.fromCallable is exactly what you need:
fun insertTopic(): Single<Long> = Single.fromCallable {
val topic = Topic(null, topicText.value!!, difficulty.value!!, false)
return#fromCallable topicDao.insert(topic)
}
Please note, that I create topic object inside lambda so that it isn't captured in a closure. Also keep in mind that fromCallable may throw UndeliverableException if you unsubscribe from Single during the lambda code execution. It will probably never happen in your specific case, but you should understand RxJava2 error handling design.
While iterating and fetching web responses the chain stops when it encounters an error.
I used .onErrorResumeNext(Observable.empty()) to keep the iteration going but want to do some error handling too. How can this be done?
.getRepos()
.flatMap { itemList ->
//fetches all repos
Observable.fromIterable(itemList)
}
.concatMapEager { githubItem ->
//fetches commits of each repos
networkModule.getCommits().map { commitItem ->
dataBaseModule.insertRepo(commitItem)
}.onErrorResumeNext(Observable.empty())
//here I want to take some action instead of just passing an empty observable
}
You can use the doOnError() operator just before the onErrorResumeNext() to perform an action.
...
.doOnError( error -> {} )
.onErrorResumeNext( Observable.empty() )
...
I am new to RxJava. My experience is mainly from those tutorials:
Here, and here.
Now, I found myself in a situation where I have API call with Retrofit2 that will return an Obesrvable<AccessToken>. The client will call this service as follows:
public Observable<TokenResult> authenticateWithClientCredentials() {
return authService.authenticate("client_credentials").take(1);
}
Where a class called Authenticator will call #authenticateWithClientCredentials() from the client.
What I would like to achieve is to return an Observable<Boolean> from the Authenticator class, once the API call is finished, i.e. inside onComplete() to indicate that the access token has been fetched and saved in cache inside the Authenticator class successfully.
I tried the defer operator but I'm still lost.
EDIT
I know I can pass a callback in the parameters, but isn't the idea of Rx to replace the old classic callbacks approach?
If I have understood your question correctly, then this is what you are searching:
client.authenticateWithClientCredentials()
.map(token -> {
// save `token` into cache
return token;
})
.flatMap(integer -> Observable.just(Boolean.TRUE))
.subscribe(aBoolean -> {
// success
}, throwable -> {
// error
});
I do not know why exactly you want Observable<Boolean>, Completable is much more preferred in this case, because either the stream has successfully completed or no. Nevertheless, posted the solution using Observable<Boolean> return type.
Here's the Completable approach:
client.authenticateWithClientCredentials()
.flatMapCompletable(integer -> {
// save `token` into cache
return Completable.complete();
})
.subscribe(() -> {
// success
}, throwable -> {
// error
});