简体   繁体   中英

Emit null from Observable in RxJava

I have been trying for quite some time but I am unable to wrap my head around processing null values in RxJava & Kotlin

I have a Room database which returns a list of some entities (topics) from database. I need to pick one random item from the list or process different action if the list is empty.

After reading through various answers on SO and trying different approaches. I tried using Optional :

fun getRandomTopic(): Single<Optional<Topic>> {
        return topicDao.getAll().flatMap { topics ->
            if (topics.isEmpty()) {
                Single.just(Optional.ofNullable(null))
            }

            val index = (Math.random() * topics.size).toInt()
            Single.just(Optional.of(topics[index]))
        }
    }

This function is observed in my activity:

viewModel.getRandomTopic()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { result ->
                    if (result.isPresent) {
                        viewModel.currentTopic.postValue(result.get())
                    } else {
                        Toast.makeText(this, "No topic found", Toast.LENGTH_SHORT).show()
                    }
                })

However, this always triggers either null pointer exception or IndexOutOfBoundsException :

io.reactivex.exceptions.OnErrorNotImplementedException: Index: 0, Size: 0
        at io.reactivex.internal.functions.Functions$14.accept(Functions.java:229)
        at io.reactivex.internal.functions.Functions$14.accept(Functions.java:226)
        at io.reactivex.internal.observers.ConsumerSingleObserver.onError(ConsumerSingleObserver.java:44)
        at io.reactivex.internal.operators.single.SingleObserveOn$ObserveOnSingleObserver.run(SingleObserveOn.java:79)
        at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:111)
        at android.os.Handler.handleCallback(Handler.java:761)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:156)
        at android.app.ActivityThread.main(ActivityThread.java:6523)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
     Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.ArrayList.get(ArrayList.java:411)
        at cz.xxx.TopicViewModel$getRandomTopic$1.apply(TopicViewModel.kt:31)
        at cz.xxx.TopicViewModel$getRandomTopic$1.apply(TopicViewModel.kt:17)

It seems that the condition

if (topics.isEmpty()) {
       Single.just(Optional.ofNullable(null))
}

is somehow ignored and the statement continues even when the array is empty. What am I doing wrong here?

You have basically described where problem is. If you take a look at source code in Android Studio, expression Single.just(Optional.ofNullable(null)) is not evaluated as return value of the .flatMap() .

AS屏幕截图

Only last value of an lambda is. I recommend you to write down return statements like return@something to make a code more clear and understandable. Solution?

fun getRandomTopic(): Single<Optional<Topic>> {
    return topicDao.getAll().flatMap { topics ->
        return@flatMap if (topics.isEmpty()) {
            Single.just(Optional.ofNullable<Topic>(null))
        } else {
            val index = (Math.random() * topics.size).toInt()
            Single.just(Optional.of(topics[index]))
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM