webentwicklung-frage-antwort-db.com.de

RxJava - holt jeden Eintrag in der Liste

Ich habe eine Methode, die einen Observable<ArrayList<Long>> Zurückgibt, bei der es sich um IDs einiger Elemente handelt. Ich möchte diese Liste durchgehen und jeden Artikel mit einer anderen Methode herunterladen, die Observable<Item> Zurückgibt.

Wie würde ich das mit RxJava-Operatoren machen?

39
k_wisniewski

Hier ist ein kleines eigenständiges Beispiel

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

Bitte beachten Sie, dass Observable.just(Arrays.<Integer>asList(1, 2, 3)) eine einfache Darstellung von Observable<ArrayList<Long>> Aus Ihrer Frage ist. Sie können es durch Ihr eigenes Observable in Ihrem Code ersetzen.

Dies sollte Ihnen die Grundlage geben, was Sie brauchen.

p/s: Verwenden Sie für diesen Fall die flatMapIterable -Methode, da sie zu Iterable gehört, wie nachstehend erläutert:

/**
 * 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

Alternativ zu flatMapIterable können Sie dies mit flatMap tun:

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
}

Persönlich denke ich, dass .flatMap(numberList -> Observable.fromIterable(numberList)) leichter zu lesen und zu verstehen ist als .flatMapIterable(numberList -> numberList ).

Der Unterschied scheint die Reihenfolge zu sein (RxJava2):

  • Observable.fromIterable: Konvertiert eine Iterable-Sequenz in eine ObservableSource, die die Elemente in der Sequenz ausgibt.
  • Observable.flatMapIterable: Gibt ein Observable zurück, das jedes von der ObservableSource-Quelle ausgegebene Element mit den Werten in einem Iterable zusammenführt, das dem von einem Selektor generierten Element entspricht .

Mit Methodenreferenzen sieht das so aus:

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

Verwenden Sie ein Transformer , das die Quelle Observable ändert und ein flatMap mit einer Funktion darauf aufruft. Sie können sich dies als einen zweistufigen Prozess vorstellen:

  1. Die Funktion nimmt jeden ausgegebenen Gegenstand (ein Iterable<T>) und gibt es als Observable<T>
  2. flatMap nimmt jede dieser ausgesendeten Observable<T> Objekte und fügt sie zu einem einzigen Observable<T>

Der Transformer sieht folgendermaßen aus:

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

Sobald Sie Ihren Transformer erstellt haben, können Sie compose verwenden, um die Transformation auf die beobachtbare Quelle anzuwenden:

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

Der Vorteil der Verwendung eines compose mit einem Transformer anstelle eines flatMap mit einem Func1 Wenn Sie in Zukunft eine Liste erneut reduzieren müssen, müssen Sie nicht einmal darüber nachdenken, welchen Operator Sie verwenden sollen (map? flatMap? concatMap?). Mit anderen Worten, die Flatmap-Operation wird in die FlattenTransform-Klasse eingebunden und dieses Detail wird abstrahiert.

Transformatoren bieten auch andere Vorteile, z. B. die Möglichkeit, mehrere Vorgänge miteinander zu verketten.

4
weefbellington

In Kotlin benutze flattenAsFlowable:

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