Reputation: 6050
I am using RxJava2 in my android project.
I am using the following code to create the Observable
public Observable<AlbumDetails> loadAlbumFromAlbumId(final String albumId) {
return Observable.fromCallable(new Callable<AlbumDetails>() {
@Override
public AlbumDetails call() throws Exception {
AlbumDetails albumDetails = getAlbumDetails(albumId);
return albumDetails;
});
}
From the observable, I am getting following error in the onError method of DisposableObserver
Callable returned null
This didn't use to happend when using RxJava.
Upvotes: 17
Views: 16164
Reputation: 775
I came up with this workaround :
First created a wrapper class:
class NullableObject<T>(val value: T?)
Then created an extension function for Single like this:
inline fun <T> runInSingle(
crossinline block: () -> T,
crossinline callback: (T?) -> Unit,
crossinline onError: (Throwable) -> Unit) {
Single.fromCallable {
return@fromCallable block().toNullableObject()
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result, error ->
if (error != null) {
onError(error)
} else {
callback(result.value)
}
}
}
fun <T> T.toNullableObject(): NullableObject<T> = NullableObject(this)
How to use :
runInSingle({
doSomethingInBackgroundWhichMayReturnNull()
}, { result ->
if(result != null) {
doSomethingWithTheNonNullResult()
} else {
doSomethingWithTheNullResult()
}
}, { error ->
error.printStackTrace()
})
Upvotes: 0
Reputation: 19623
I used to have code like this:
Observable<R> obs = Observable.fromCallable(this::work);
work() may return null
and as you noted, RxJava 2 is not happy with this.
Observable<R> obs = Maybe.fromCallable(this::work).toObservable();
In this way, the end consumer - the observer - will only kick off if work() had anything to return. If work() returned null then it's the same as subscribing to an empty Observer; nothing happens.
Observable<R> obs = Observable.create(e -> {
R r = work();
if (r != null) {
e.onNext(r);
}
e.onComplete();
});
Wrapping each emission in an Optional
= bad idea for a number of reasons.
Mainly because I only use Optional in my APIs if it is expected and sort of the "normal" scenario that you get a null every now and then. If null is not expected or very rare, maybe as a direct result of another API call done by the same consumer, then I return null and let the consumer deal with it in these exceptional cases rather than having every consumer deal with Optional all over the place.
Upvotes: 21
Reputation: 58
Assuming that getAlbumDetails(albumId)
method returns null
in case of the data is not available or error, and you are willing to just gracefully complete your stream in such situation, you could use something like this:
public Observable<AlbumDetails> loadAlbumFromAlbumId(String albumId) {
return Maybe.fromCallable(() -> getAlbumDetails(albumId))
.onErrorComplete()
.toObservable();
};
I've replaced the anonymous class with lambda for better readability, but you can keep it as is, of course.
Upvotes: 2
Reputation: 36
You can also wrap the return value in an Optional (Java 8). And the map that within your subscription using optional.isPresent() and optional.get().
Upvotes: 0
Reputation: 348
RxJava 2.x no longer accepts null values without implementing error handler
example
Observable.fromCallable(() -> null)
.subscribe(System.out::println, Throwable::printStackTrace);
Observable.just(1).map(v -> null)
.subscribe(System.out::println, Throwable::printStackTrace);
Solution for your questian
When you call/subscribe this method do like bellow
Observable<AlbumDetails> testObservable= Observable.create(emitter -> { ... });
testObservable.subscribe(t -> System.out.print(t),Throwable::printStackTrace);
Upvotes: 3
Reputation: 6050
As given here in the wiki :
RxJava 2.x no longer accepts null values. Having null as a stream element causes a NullPointerException
immediately and thus the onError method is called.
This has been done mainly to avoid crashes due to NullPointerException
in the apps.
I solved this by a rather simple approach. I checked if the value was null, and if it was, I replaced the null with a placeholder object.
Other more sophisticated approaches also exist that tackle this issue, but for my use case, this simple approach was enough.
Upvotes: 9