web-dev-qa-db-fra.com

RxJava - récupère chaque élément de la liste

J'ai une méthode qui retourne un Observable<ArrayList<Long>>, qui sont les identifiants de certains éléments. J'aimerais parcourir cette liste et télécharger chaque élément à l'aide d'une autre méthode qui renvoie Observable<Item>.

Comment pourrais-je faire cela en utilisant des opérateurs RxJava?

39
k_wisniewski

Voici un petit exemple autonome

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

Veuillez noter que Observable.just(Arrays.<Integer>asList(1, 2, 3)) est une simple représentation de Observable<ArrayList<Long>> De votre question. Vous pouvez le remplacer par votre propre observable dans votre code.

Cela devrait vous donner la base de ce dont vous avez besoin.

p/s: Utilisez la méthode flatMapIterable pour ce cas car il appartient à Iterable comme expliqué ci-dessous:

/**
 * 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>
51
Miguel Lavigne

Au lieu de flatMapIterable, vous pouvez le faire avec 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
}

Personnellement, je pense que .flatMap(numberList -> Observable.fromIterable(numberList)) est plus facile à lire et à comprendre que .flatMapIterable(numberList -> numberList ).

La différence semble être l'ordre (RxJava2):

  • Observable.fromIterable: Convertit une séquence Iterable en une source Observable qui émet les éléments de la séquence.
  • Observable.flatMapIterable: Retourne un observable qui fusionne chaque élément émis par le source source ObservableSource avec les valeurs d'un Iterable correspondant à cet élément généré par un sélecteur. .

En utilisant des références de méthodes, cela ressemble à:

Observable.just(Arrays.asList(1, 2, 3))
            .flatMap(Observable::fromIterable)
            .flatMap(this::downloadFoo)
5
patrickf

Utilisez un Transformer qui modifie l’observable source en appelant un flatMap avec une fonction. Vous pouvez considérer cela comme un processus en deux étapes:

  1. La fonction prend chaque élément émis (un Iterable<T>) et le réémet comme un Observable<T>
  2. flatMap prend chacune de ces émises Observable<T> objets et les fusionne en un seul Observable<T>

Le transformateur ressemble à ceci:

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

Une fois votre Transformer créé, vous pouvez utiliser compose pour appliquer la transformation à la source observable:

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

L’avantage d’utiliser un compose avec un Transformer au lieu d’un flatMap avec un Func1 _ est-ce que si à l'avenir, si vous avez besoin d'aplatir une nouvelle liste, vous n'aurez même pas à penser à l'opérateur à utiliser (map? flatMap? concatMap?). En d'autres termes, l'opération flatmap est intégrée à la classe FlattenTransform et ce détail est extrait par résumé.

Les transformateurs présentent également d'autres avantages, tels que la possibilité d'enchaîner plusieurs opérations.

4
weefbellington

En Kotlin, utilisez flattenAsFlowable:

repository.getFlowableData(id)
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.computation())
    .toList()
    .flattenAsFlowable { it }
    .map { someMethod(it) }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ },
        { onError(it) })
1
CoolMind