Search code examples
androidexceptionrx-javaandroid-roomrx-android

RxJava Single and Room Error handling Problem


I am having this issue regarding empty database return with Room and RxJava Single. I know that my database is empty, so I am expecting to get an empty return when I trigger

@Query("SELECT * FROM Times WHERE timestamp = :timestamp")
fun getTimes(timestamp: String): Single<Times>

The problem is when I call this function as below

timeDao.getTimes("1398332113")
       .observeOn(AndroidSchedulers.mainThread())
       .subscribeOn(Schedulers.io())
       .doOnError { Log.e("Single", "Returned null") }
       .doOnSuccess { result -> times = result}
       .subscribe()

The subscriber is indeed calling doOnError method like

E/Single: Returned null

but still returning an exception and crash

W/System.err: io.reactivex.exceptions.OnErrorNotImplementedException: Query returned empty result set: SELECT * FROM Times WHERE timestamp = ?

I have seen so many similar questions on StackOverflow, but couldn't find an answer. What am I am doing wrong?


Solution

  • First a solution for your problem. Since version 2.1.0-alpha01 Room supports the Maybe return type which is perfect for modelling your problem.

    From the documentation:

    The Maybe operates with the following sequential protocol: onSubscribe (onSuccess | onError | onComplete)?

    When your the item is in the db: onSuccess will be called with the data. If the db is empty onComplete will be called. onError is self explanatory.

    Replacing the Single with Maybe in the Dao class will work.

    Further notes:

    timeDao.getTimes("1398332113")
           .observeOn(AndroidSchedulers.mainThread())
           .subscribeOn(Schedulers.io())
           .doOnError { Log.e("Single", "Returned null") }
           .doOnSuccess { result -> times = result}
           .subscribe()
    

    doOnError

    doOnError will execute the the lambda in case of en error, but it will still emit the error down the chain. Your program crashes because it doesn't handle errors (the subscribe() is empty).

    You could do subscribe({/* doing nothing for success */}, {e -> Log.e("TAG", "Empty DB")}) to prevent the crash.

    default value

    If the goal is to return some default value in case the DB is empty then you will want to use onErrorReturn so that the chain continues. Take a look at this blog post for more info.

    Null values

    The default value can't be null, the Reactive Streams specification (RxJava2 implements it) doesn't support null values.