繁体   English   中英

如何在RxJava 2中结合动态数量的Observable?

[英]How to combine a dynamic number of Observables in RxJava 2?

我陷入了一个很奇怪的问题,要求您的帮助。

总体设置如下:我有一堆可以在运行时出现或消失的数据提供程序。 这些提供程序提供的数据令人惊讶(建模为io.reactivex.Observable (确切地说,作为BehaviorSubject ,记住新订户的最新数据。)

现在,我需要合并所有当前数据提供者的数据,以便获得一个新的“主要” Observable,每当任何数据提供者的可观察变化或新提供者出现(或旧提供者消失)时,该值都会更新。

到目前为止,这听起来像是合并,但是对于主要的Observable,我需要将所有提供者的数据组合在一起,无论发生什么变化,每个提供者各自的最后状态。 结合使用Observable.combineLatest ,这种组合对于预先已知的非动态提供者非常有效。

但是,当我将该方法嵌入到动态上下文中时,就会出现问题,从而为添加或删除的提供程序添加列表。 然后,对提供者的“可观察的”之一的更新不仅触发了预期的一个更新,还触发了多个更新,其中一些更新仅包含部分数据。

看看下面的示例(使用RxJava 2.1.9,功能齐全),该示例应阐明我的问题。 注释通过println()完成,因此生成的输出也可读。 第一部分是静态的,没有添加或删除提供程序,并且按预期工作。 第二部分是奇怪的事情...

感谢您提供解决此问题的其他想法或帮助-谢谢!

import io.reactivex.Observable;
import io.reactivex.functions.Function;
import io.reactivex.subjects.BehaviorSubject;
import io.reactivex.subjects.Subject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CombineLatestWithDynamicSources {

    static final Function<Object[], List<String>> STRING_COMBINER = objects -> Arrays.stream(objects)
                                                                                     .map(Object::toString)
                                                                                     .collect(Collectors.toList());

    public static void main(String[] args) {

        System.out.println("*** STATIC ***");
        staticCombineWorksAsExpected();

        System.out.println("\n*** DYNAMIC ***");
        dynamicCombineBehavesWeird();

    }

    static void staticCombineWorksAsExpected() {

        Subject<String> subjectA = BehaviorSubject.createDefault("A.1");
        Subject<String> subjectB = BehaviorSubject.createDefault("B.1");

        List<Subject<String>> subjects = Arrays.asList(subjectA, subjectB); // potentially more...

        Observable<List<String>> combined = Observable.combineLatest(subjects, STRING_COMBINER);

        System.out.println("initial values:");
        combined.subscribe(strings -> System.out.println(">> Combined: " + strings));

        System.out.println("updating A:");
        subjectA.onNext("A.2");

        System.out.println("updating B:");
        subjectB.onNext("B.2");

        System.out.println("\n... works as expected, but adding subjects isn't possible in this setting.");
    }

    static void dynamicCombineBehavesWeird() {

        List<Subject<String>> subjectsList = new ArrayList<>();
        Subject<List<Subject<String>>> subjectsObservable = BehaviorSubject.createDefault(subjectsList);

        System.out.println("subjects are initially empty:");
        subjectsObservable.subscribe(subjects -> System.out.println(">> Subjects: " + subjects));

        Observable<List<String>> combined = subjectsObservable.flatMap(
                subjects -> Observable.combineLatest(subjects, STRING_COMBINER));

        combined.subscribe(strings -> System.out.println(">> Combined: " + strings));

        System.out.println("add Subject A, providing default value 'A.1' - as expected:");
        Subject<String> subjectA = BehaviorSubject.createDefault("A.1");
        subjectsList.add(subjectA);
        subjectsObservable.onNext(subjectsList);

        System.out.println("updating A - also as expected:");
        subjectA.onNext("A.2");

        System.out.println("add Subject B, providing default value 'B.1' - as expected, now both subject's last values show up:");
        Subject<String> subjectB = BehaviorSubject.createDefault("B.1");
        subjectsList.add(subjectB);
        subjectsObservable.onNext(subjectsList);

        System.out.println("updating A again - I'd expect the second result only! Why is there '[A.3]' popping up before the expected result?!");
        subjectA.onNext("A.3");

        System.out.println("This doesn't happen on updating B...:");
        subjectB.onNext("B.2");

        System.out.println("digging deeper, add Subject C, providing default value 'C.1' - as expected again:");
        Subject<String> subjectC = BehaviorSubject.createDefault("C.1");
        subjectsList.add(subjectC);
        subjectsObservable.onNext(subjectsList);

        System.out.println("Now update A - three results pop up, only the last is expected!");
        subjectA.onNext("A.4");

        System.out.println("update B, which now emits also two results - last expected only:");
        subjectB.onNext("B.3");

        System.out.println("update C works as expected:");
        subjectC.onNext("C.2");

        System.out.println("\n... huh? Seems on updating the first source, the combined results gets computed for the first, " + "then for the first and second, then for first, second and third (and so on?) source...");

    }

}

...产生以下输出:

*** STATIC ***
initial values:
>> Combined: [A.1, B.1]
updating A:
>> Combined: [A.2, B.1]
updating B:
>> Combined: [A.2, B.2]

... works as expected, but adding subjects isn't possible in this setting.

*** DYNAMIC ***
subjects are initially empty:
>> Subjects: []
add Subject A, providing default value 'A.1' - as expected:
>> Subjects: [io.reactivex.subjects.BehaviorSubject@4157f54e]
>> Combined: [A.1]
updating A - also as expected:
>> Combined: [A.2]
add Subject B, providing default value 'B.1' - as expected, now both subject's last values show up:
>> Subjects: [io.reactivex.subjects.BehaviorSubject@4157f54e, io.reactivex.subjects.BehaviorSubject@90f6bfd]
>> Combined: [A.2, B.1]
updating A again - I'd expect the second result only! Why is there '[A.3]' popping up before the expected result?!
>> Combined: [A.3]
>> Combined: [A.3, B.1]
This doesn't happen on updating B...:
>> Combined: [A.3, B.2]
digging deeper, add Subject C, providing default value 'C.1' - as expected again:
>> Subjects: [io.reactivex.subjects.BehaviorSubject@4157f54e, io.reactivex.subjects.BehaviorSubject@90f6bfd, io.reactivex.subjects.BehaviorSubject@47f6473]
>> Combined: [A.3, B.2, C.1]
Now update A - three results pop up, only the last is expected!
>> Combined: [A.4]
>> Combined: [A.4, B.2]
>> Combined: [A.4, B.2, C.1]
update B, which now emits also two results - last expected only:
>> Combined: [A.4, B.3]
>> Combined: [A.4, B.3, C.1]
update C works as expected:
>> Combined: [A.4, B.3, C.2]

... huh? Seems on updating the first source, the combined results gets computed for the first, then for the first and second, then for first, second and third (and so on?) source...

我的合并问题使我不得不待了好几天才问这里-但是即使在我提出问题之后,它也没有让我离开...现在终于,我可以继续研究,并且有解决方案。 问题是flatmap 以前订阅的未处置。 天哪!

首先,我将链条分开以了解运动部件:

List<Subject<String>> subjectsList = new ArrayList<>();
Subject<List<Subject<String>>> subjectsObservable = BehaviorSubject.createDefault(subjectsList);

final Disposable[] disposable = new Disposable[]{Disposables.empty()};

// combined now is a subject to push updates into
Subject<List<String>> combined = BehaviorSubject.createDefault(Collections.emptyList());

subjectsObservable.subscribe(subjects -> {
    Observable<List<String>> combSubj = Observable.combineLatest(subjects, STRING_COMBINER);

    // here's the key: I remember the previous subscription and dispose it on update.
    disposable[0].dispose();
    disposable[0] = combSubj.subscribe(strings -> combined.onNext(strings));
});

combined.subscribe(strings -> System.out.println(">> Combined: " + strings));

最后,更加整洁,更接近我原来的(无效)解决方案:

// remember the disposables, start with an empty to avoid NullPointerExceptions
Disposable[] prevDisposable = new Disposable[]{Disposables.empty()};
Observable<List<String>> combined = subjectsObservable.flatMap(
        subjects -> Observable.combineLatest(subjects, STRING_COMBINER)
                              // dispose the previous subscription to combineLatest's Observable and remember the new one
                              .doOnSubscribe(disposable -> {
                                    prevDisposable[0].dispose();
                                    prevDisposable[0] = disposable;
                              })
        );

combined.subscribe(strings -> System.out.println(">> Combined: " + strings));

您可以使用.concatMap操作者

val o1 = Observable.just(0,1,2)
val o2 = Observable.just(3,4,5)
val subject = PublishSubject.create<Observable<Int>>()

subject
        .concatMap { it }
        .subscribe { it: Int? ->
            System.out.print("$it,")
        }

subject.onNext(o1)
subject.onNext(o2)

出来是

0,1,2,3,4,5,

从那以后的一段时间-现在我知道了... ;-)

简单的解决方案-如此简单...:m

subjectsObservable.switchMap(subjects -> Observable.combineLatest(subjects, STRING_COMBINER))
                  .subscribe(strings -> System.out.println(">> Combined: " + strings));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM