Avec la version à venir RxJava2 , l’un des changements importants est que null
n’est plus accepté en tant qu’élément de flux, c’est-à-dire que le code suivant lève une exception: Observable.just(null)
Honnêtement, j'ai des sentiments partagés sur ce changement et une partie de moi comprend qu'elle appliquera des API propres, mais je peux voir un certain nombre de cas d'utilisation où cela pourrait poser problème.
Par exemple, dans mon application, j'ai un cache en mémoire:
@Nullable CacheItem findCacheItem(long id);
CacheItem peut ne pas être présent dans le cache, donc la méthode peut renvoyer une valeur null.
La façon dont il est utilisé avec Rx * - est la suivante:
Observable<CacheItem> getStream(final long id) {
return Observable.fromCallable(new Callable<CacheItem>() {
@Override public CacheItem call() throws Exception {
return findCacheItem(id);
}
});
}
Donc, avec cette approche, mon flux peut devenir nul, ce qui est une situation tout à fait valide; il est donc traité correctement du côté de la réception - disons que l'interface utilisateur change d'état si l'élément n'est pas présent dans le cache:
Observable.just(user)
.map(user -> user.getName())
.map(name -> convertNameToId(name))
.flatMap(id -> getStream(id))
.map(cacheItem -> getUserInfoFromCacheItem(cacheItem))
.subscribe(
userInfo -> {
if(userInfo != null) showUserInfo();
else showPrompt();
}
);
Avec RxJava2, je ne suis plus autorisé à publier null
dans le flux. Je dois donc envelopper mon CacheItem dans une autre classe et faire en sorte que mon flux produise cet encapsuleur ou apporter des modifications architecturales assez importantes.
Emballer chaque élément de flux en une contrepartie nullable ne me convient pas.
Est-ce que je manque quelque chose de fondamental ici?
Il semble que la situation comme la mienne soit assez populaire, alors je suis curieux de savoir quelle est la stratégie recommandée pour s'attaquer à ce problème étant donné la nouvelle politique "aucun null" dans RxJava2?
EDIT Veuillez consulter la conversation suivante dans RxJava GitHub repo
Eh bien, il y a plusieurs façons de représenter ce que vous voulez.
Une option consiste à utiliser Observable<Optional<CacheItem>>
:
Observable<Optional<CacheItem>> getStream(final long id) {
return Observable.defer(() -> {
return Observable.just(Optional.ofNullable(findCacheItem(id)));
});
}
public static <T> Transformer<Optional<T>, T> deoptionalize() {
return src ->
src.flatMap(item -> item.isPresent()
? Observable.just(item.get())
: Observable.empty();
}
Vous utilisez ensuite .compose(deoptionalize())
pour mapper l’observable facultatif à l’observable.
Vous pouvez utiliser RxJava2-Nullable
pour gérer la valeur null dans RxJava2.
Pour votre situation, vous pouvez faire:
Observable<CacheItem> getStream(final long id) {
return RxNullable.fromCallable(() -> findCacheItem(id))
.onNullDrop()
.observable();
}
Pour invoquer showPrompt
quand c'est null, vous pouvez faire:
Observable.just(user)
.map(user -> user.getName())
.map(name -> convertNameToId(name))
.flatMap(id -> getStream(id).onNullRun(() -> showPrompt()))
.map(cacheItem -> getUserInfoFromCacheItem(cacheItem))
.subscribe(userInfo -> showUserInfo());
NullableObservable<CacheItem> getStream(final long id) {
return RxNullable.fromCallable(() -> findCacheItem(id)).observable();
}
En tant que solution supplémentaire, vous pouvez ajouter l'instance statique CacheItem.NULL et la renvoyer à l'abonné lorsqu'il n'y a pas de données en cache.
Single
.concat(loadFromMemory(), loadFromDb(), loadFromServer())
.takeFirst { it != CachedItem.NULL }
.subscribe(
La solution possible est d'utiliser Maybe.switchIfEmpty
Exemple:
public static <T> Maybe<T> maybeOfNullable(T value) {
return value == null ? Maybe.empty() : Maybe.just(value);
}
maybeOfNullable(user)
.map(user -> user.getName())
.map(name -> convertNameToId(name))
.flatMap(id -> getStream(id))
.map(cacheItem -> getUserInfoFromCacheItem(cacheItem))
// perform another action in case there are no any non null item emitted
.switchIfEmpty(Maybe.fromAction(() -> showPrompt()))
.subscribe(userInfo -> showUserInfo());