我遇到了一个非常奇怪的问题并请求你的帮助。
总体设置如下:我有一堆数据提供程序可以在运行时发生或消失。这些提供商提供了 - 大惊喜 - 数据,建模为io.reactivex.Observable
。 (确切地说:作为BehaviorSubject
,记住新订阅者的最新数据。)
现在,我需要结合所有当前数据提供者的数据,以便获得一个新的“主”Observable,当任何数据提供者的可观察变化或新的提供者出现时(或旧的提供者消失),它会得到更新。
到目前为止,这听起来像是合并,但对于主Observable,我需要所有提供商的数据,在任何变化时,每个提供商的相应最后状态。这种组合适用于使用Observable.combineLatest
预先知道的非动态提供者。
但是当我将该方法嵌入到动态上下文中时,会出现问题,并且会监听添加或删除的提供程序。 然后更新其中一个提供程序的Observable,不仅会触发一次预期的更新,还会触发多次更新,其中一些更新只包含部分数据。
看看以下(自包含,使用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...
答案 0 :(得分:0)
我的结合问题使我在这里问了好几天 - 但即使在我的问题之后,它也没有让我离开......现在终于,我可以继续挖掘它,我有一个解决方案。问题在于没有处理以前订阅里面的 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));
答案 1 :(得分:0)
从那以后的一段时间-现在我知道了...;-)
简单的解决方案-如此简单...:m
subjectsObservable.switchMap(subjects -> Observable.combineLatest(subjects, STRING_COMBINER))
.subscribe(strings -> System.out.println(">> Combined: " + strings));