PublishSubject onError function in Kotlin - android

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.

Related

Kotlin flow emits nothing when I call the function second time

I try to implement deleting user in FirebaseAuth using Kotlin flow (SharedFlow).
In onDeleteAccountClicked() there is delete() method called from FirebaseAuth which may throw AuthReauthenticationRequiredException. When the exception is thrown, app redirects to another fragment to reauthenticate, then call onDeleteAccountClicked() once again, but flow emits nothing.
ViewModel
private val _deleteAccount = MutableSharedFlow<() -> Unit>()
fun onDeleteAccountClicked() {
logd("outside the viewModelScope")
viewModelScope.launch {
logd("inside the viewModelScope")
_deleteAccount.emit {
logd("emitting log")
firebaseAuth.deleteUser()
//throw AuthReauthenticationRequiredException()
}
}
}
init {
viewModelScope.launch {
_deleteAccount
.onEach {
it()
}
.catch {
if (it is AuthReauthenticationRequiredException) {
_redirectToSignInMethodsScreen.emit(Unit)
}
}
.collect()
}
}
Logs "outside the viewModelScope" and "inside the viewModelScope" shows every time when the method is called, but "emitting log" only for the first time.
Am I even trying to do it the right way?
I just tested the code, and it works for me. I called onDeleteAccountClicked() three times with delay between calling, and all three logs "emitting log" inside emit lambda were printed. Try to remove calling firebaseAuth.deleteUser() inside emit lambda and test. Calling FirebaseUser.delete function when user is already deleted throws FirebaseAuthInvalidUserException exception. Maybe that's why you didn't see logs - because FirebaseUser.delete function throws an exception.
I think the structure you use for calling just one function is a bit complicated, I can suggest to get rid of _deleteAccount flow and just wrap firebaseAuth.deleteUser() inside try-catch (you even don't need to launch a coroutine for that):
fun onDeleteAccountClicked() {
try {
firebaseAuth.deleteUser()
} catch(e: AuthReauthenticationRequiredException) {
_redirectToSignInMethodsScreen.emit(Unit)
}
}

Catching exceptions in Kotlin Flows with .catch

I'm a bit confused on how catching exceptions exactly work in Kotlin flows.
Looking at this sample code from https://developer.android.com/kotlin/flow#exceptions.
class NewsRepository(...) {
val favoriteLatestNews: Flow<List<ArticleHeadline>> =
newsRemoteDataSource.latestNews
.map { news -> news.filter { userData.isFavoriteTopic(it) } }
.onEach { news -> saveInCache(news) }
// If an error happens, emit the last cached values
.catch { exception -> emit(lastCachedNews()) }
Does .catch catch any exceptions thrown by the following?
newsRemoteDataSource.latestNews
.map
.onEach
If an exception was caught from .map for example, does .onEach ever get run? Or does it jump straight to the .catch?
Does .catch catch any exceptions thrown by the following?
newsRemoteDataSource.latestNews
map
onEach
catch catches exception thrown from any of these, and it would cancel the flow as soon as it catches any exception.
If an exception was caught from .map for example, does .onEach ever get run? Or does it jump straight to the .catch?
Order of execution is from left to right, so in this case, if an exception occurs in map it would instantly catch the exception and cancel the flow, and it won't run onEach. If you move onEach before map, it would do the vice versa.
If any exception occurs in newsRemoteDataSource.latestNews, it won't run either of map and onEach.

How to handle a subcribe() method correctly?

I've been having troubles with subscribe() method in my code (debug console's message below)
io.reactivex.exceptions.OnErrorNotImplementedException: 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 | Expected a string but was BEGIN_OBJECT at line 1 column 2 path $
and I can't figure out how to make it right, there is my part of code where it starts
private fun startSearch(query: String) {
disposables.addAll(IMyService.searchCourse(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({ courses ->
adapter = CourseAdapter(baseContext, courses)
recycler_search.adapter = adapter
}, {
Toast.makeText(this, "Not found", Toast.LENGTH_LONG).show()
}))
}
private fun getAllCourses() {
disposables.addAll(IMyService.coursesList
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({ courses ->
adapter = CourseAdapter(baseContext, courses)
recycler_search.adapter = adapter
}, {
Toast.makeText(this, "Not found", Toast.LENGTH_LONG).show()
}))
}
and there is the full code
parameters
In reactive programming, passing a subscriber to an Observable should entail how to deal with three cases:
onSuccess
onError
onFailure
If however, you simply want to pass a subscriber which you know for sure will not have any errors or any failures and certain that it will always succeed, then simply try onSuccess or onFailure as mentioned by #EpicPandaForce. A good practice however is to always implement the three cases as you never know.

Rx Exception Handling Fail

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.

Handling Error RXJava Android with Kotlin

Hi I'm new with RxJava and Kotlin and I loose some concepts about it.
I have "api" like this:
interface VehiclesService {
#GET("/vehicles/")
fun getVehicles(): Single<List<Vehicle>>
}
Then I create the retrofit client, etc.. like this:
var retrofit = RetrofitClient().getInstance()
vehiclesAPI = retrofit!!.create(VehiclesService ::class.java)
finally I do the call:
private fun fetchData() {
compositeDisposable.add(vehiclesAPI .getVehicles()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { vehicles -> displayData(vehicles) }
)
}
And here is where I have the error when I try to launch:
The exception was not handled due to missing onError handler in the subscribe() method call
I know that the error is quite explicit. So I know what is missing, but what I don't know is HOW to handle this error.
I tried adding : .doOnError { error -> Log.d("MainClass",error.message) } but still telling same error message.
You can pass another lambda to subscribe to handle the errors for a specific stream like this:
private fun fetchData() {
compositeDisposable.add(vehiclesAPI .getVehicles()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( { vehicles -> displayData(vehicles) }, { throwable -> //handle error } )
)
}
P.S: doOnError and other Side Effect operators, will not affect the stream in anyway, they just anticipate the values emitted for side-effect operations like logging for example.

Categories

Resources