RxJava - выбор каждого элемента в списке
У меня есть метод, который возвращает Observable<ArrayList<Long>>
, которые являются идентификаторами некоторых элементов. Я бы хотел просмотреть этот список и загрузить каждый элемент с помощью другого метода, который возвращает Observable<Item>
.
Как мне это сделать с помощью операторов RxJava?
Ответы
Ответ 1
Здесь небольшой самодостаточный пример
public class Example {
public static class Item {
int id;
}
public static void main(String[] args) {
getIds()
.flatMapIterable(ids -> ids) // Converts your list of ids into an Observable which emits every item in the list
.flatMap(Example::getItemObservable) // Calls the method which returns a new Observable<Item>
.subscribe(item -> System.out.println("item: " + item.id));
}
// Simple representation of getting your ids.
// Replace the content of this method with yours
private static Observable<List<Integer>> getIds() {
return Observable.just(Arrays.<Integer>asList(1, 2, 3));
}
// Replace the content of this method with yours
private static Observable<Item> getItemObservable(Integer id) {
Item item = new Item();
item.id = id;
return Observable.just(item);
}
}
Обратите внимание, что Observable.just(Arrays.<Integer>asList(1, 2, 3))
является простым представлением Observable<ArrayList<Long>>
из вашего вопроса. Вы можете заменить его своим собственным наблюдаемым в своем коде.
Это должно дать вам основу того, что вам нужно.
p/s: используйте метод flatMapIterable
для этого случая, так как он принадлежит Iterable
, как показано ниже:
/**
* Implementing this interface allows an object to be the target of
* the "for-each loop" statement. See
* <strong>
* <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides /language/foreach.html">For-each Loop</a>
* </strong>
*
* @param <T> the type of elements returned by the iterator
*
* @since 1.5
* @jls 14.14.2 The enhanced for statement
*/
public interface Iterable<T>
Ответ 2
Используйте Transformer, который изменяет источник Observable, вызывая flatMap
на нем с помощью функции. Вы можете думать об этом как о двухэтапном процессе:
- Функция принимает каждый испущенный элемент (a
Iterable<T>
) и переизлучает его как Observable<T>
-
flatMap
принимает каждый из этих испущенных объектов Observable<T>
, объединяет их в один Observable<T>
Трансформатор выглядит следующим образом:
public class FlattenTransform<T> implements Observable.Transformer<Iterable<T>, T> {
@Override
public Observable<? extends T> call(Observable<? extends Iterable<T>> source) {
return source.flatMap(new Func1<Iterable<T>, Observable<T>>() {
@Override
public Observable<T> call(Iterable<T> values) {
return Observable.from(values);
}
});
}
}
Как только вы создали свой трансформатор, вы можете использовать compose
для применения преобразования в наблюдаемом источнике:
public class Example {
private static final ArrayList<Long> sourceList = new ArrayList<>(Arrays.asList(new Long[] {1L,2L,3L}));
private static final Observable<ArrayList<Long>> listObservable = Observable.just(sourceList);
private static final FlattenTransform<Long> flattenList = new FlattenTransform<Long>();
public static void main(String[] args) {
listObservable.compose(flattenList).subscribe(printItem);
}
private static Action1<Long> printItem = new Action1<Long>() {
@Override
public void call(Long item) {
System.out.println("item: " + item);
}
};
}
Преимущество использования compose
с Transformer
вместо flatMap
с Func1
заключается в том, что если в будущем, если вам нужно снова сгладить список, вам даже не придется думать о том, какой оператор использовать (map? flatMap? concatMap?). Другими словами, операция flatmap выпекается в классе FlattenTransform и эта деталь абстрагируется.
Трансформаторы также имеют другие преимущества, такие как возможность объединения нескольких операций.
Ответ 3
В качестве альтернативы flatMapIterable
вы можете сделать это с помощью flatMap
:
Observable.just(Arrays.asList(1, 2, 3)) //we create an Observable that emits a single array
.flatMap(numberList -> Observable.fromIterable(numberList)) //map the list to an Observable that emits every item as an observable
.flatMap(number -> downloadFoo(number)) //download smth on every number in the array
.subscribe(...);
private ObservableSource<? extends Integer> downloadFoo(Integer number) {
//TODO
}
Лично я думаю, что .flatMap(numberList -> Observable.fromIterable(numberList))
легче читать и понимать, чем .flatMapIterable(numberList -> numberList )
.
Различие похоже на порядок (RxJava2):
- Observable.fromIterable: Преобразует итерационную последовательность в наблюдаемый источник, который испускает элементы в последовательности.
- Observable.flatMapIterable: Возвращает Observable, который объединяет каждый элемент, испускаемый источником ObservableSource, со значениями в Iterable, соответствующими этому элементу, который генерируется селектором.
Используя ссылки на методы, это выглядит так:
Observable.just(Arrays.asList(1, 2, 3))
.flatMap(Observable::fromIterable)
.flatMap(this::downloadFoo)