2016-09-29 5 views
11

С предстоящим RxJava2 release одной из важных изменений является то, что null больше не принимается в качестве потока элемента, то есть следующий код будет сгенерировано исключение: Observable.just(null)Обращение нуль в RxJava2

Честно говоря, у меня смешанные чувства по поводу этого изменения и часть меня понимает, что она будет применять чистые API-интерфейсы, но я вижу несколько вариантов использования, когда это может быть проблемой.

Например, в моем приложении у меня есть в памяти кэш:

@Nullable CacheItem findCacheItem(long id); 

CacheItem не может присутствовать в кэше, поэтому метод может возвращать нулевое значение.

Так оно используется с Rx * - заключается в следующем:

Observable<CacheItem> getStream(final long id) { 
    return Observable.fromCallable(new Callable<CacheItem>() { 
     @Override public CacheItem call() throws Exception { 
      return findCacheItem(id); 
     } 
    }); 
} 

Так с этим подходом, я мог бы получить нуль в моем потоке, который полностью действует положение, поэтому он правильно обрабатывается на приемной стороне - скажем UI изменяет свое состояние, если элемент отсутствует в кэше:

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(); 
       } 
     ); 

с RxJava2 я больше не разрешается размещать null вниз по течению, так что либо нужно обернуть CacheItem в какой-нибудь другой класс и сделать мой поток создает эту оболочку вместо сделать довольно большие архитектурные изменения.

Обертка каждого элемента потока в нулевом эквиваленте выглядит не так.

Я пропустил что-то фундаментальное здесь?

Похоже, ситуация, подобная моей, довольно популярна, поэтому мне интересно, какая рекомендуемая стратегия для решения этой проблемы задана новой политикой «нет нуля» в RxJava2?

EDIT Пожалуйста, смотрите последующую беседу в RxJava GitHub repo

ответ

15

Ну, есть несколько способов представления, что вы хотите.

Одним из вариантов является использование 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(); 
} 

Вы затем использовать .compose(deoptionalize()) к карте от необязательное к не опциональным Observable.

1

В качестве другого решения вы можете добавить статический экземпляр CacheItem.NULL, и вернуть его к абоненту, когда нет кэшированные данные

Single 
    .concat(loadFromMemory(), loadFromDb(), loadFromServer()) 
    .takeFirst { it != CachedItem.NULL } 
    .subscribe(
0

Использование Optional является обычным способом, но я не люблю писать Optional... везде. Поэтому я пишу RxNullable для обработки нулевого значения в RxJava2. See it on github.

Для вашей ситуации, вы можете сделать:

Observable<CacheItem> getStream(final long id) { 
    return RxNullable.fromCallable(() -> findCacheItem(id)) 
        .onNullDrop() 
        .observable(); 
} 

Oh!Он должен вызывать showPrompt когда это нуль, так что вы должны сделать:

NullableObservable<CacheItem> getStream(final long id) { 
    return RxNullable.fromCallable(() -> findCacheItem(id)).observable(); 
} 

Observable.just(user) 
      .map(user -> user.getName()) 
      .map(name -> convertNameToId(name)) 
      .flatMap(id -> getStream(id).onNullRun(() -> showPrompt())) 
      .map(cacheItem -> getUserInfoFromCacheItem(cacheItem)) 
      .subscribe(userInfo -> showUserInfo()); 

Надеется, что это может помочь вам. (И вы можете навестить меня на github)

Смежные вопросы