在RxJava中重绕一个Observable?

时间:2015-10-24 04:05:59

标签: java reactive-programming rx-java

我一直在将RxJava和反应式编程应用于命令行应用程序。我发现了一些非常棒的设计模式,它们真正简化了命令行界面的代码。

然而,我有一件事在挣扎。通常我会在一系列运算符中向值发送值,并concatMap() Observable.create()来征求用户对发出值的输入。

这很容易,直到我想提供一种方法来回到之前的发射。它开始变得非常混乱,尽管我努力,但我知道这不能100%正确地工作。我想要用户输入" BACK"通过从缓存请求它来尝试倒回一个发射。是否有更好的操作员或变压器来回绕链中的先前点的排放并重新发送它们?

public class Test {

    public static void main(String[] args) {

        final Scanner scanner = new Scanner(System.in);
        final List<String> cache = new ArrayList<>();
        final AtomicInteger cursorIndex = new AtomicInteger(-1);


        Observable.just(
                "What is your name?",
                "What is your quest?",
                "What is your favorite color?",
                "What is the capital of Assyria?",
                "What is the velocity of an unladen swallow?"
        )
        .doOnNext(cache::add)
        .concatMap(q -> {
            int cursor = cursorIndex.get();
            if (cursor >= 0 && cursor < cache.size() - 2) {
                return Observable.just(cache.get(cursor));
            } else {
                cursorIndex.incrementAndGet();
                return Observable.just(q);
            }
        })
        .doOnNext(System.out::println)
        .concatMap(q -> Observable.create(s -> {
                    String answer = scanner.nextLine().trim();

                    if (answer.toUpperCase().equals("BACK")) {
                        cursorIndex.decrementAndGet();
                    }
                    s.onNext(answer);
                    s.onCompleted();
                })
        ).filter(s -> !s.equals("BACK"))
         .subscribe(q -> System.out.println("You said: " + q));

    }
}
    What is your name?
    Sir Lancelot
    You said: Sir Lancelot
    What is your quest?
    To seek the holy grail
    You said: To seek the holy grail
    What is your favorite color?
    BACK
    What is your quest?

2 个答案:

答案 0 :(得分:1)

我要做的是将索引作为BehaviorSubject并在我必须在问题之间切换时调用onNext:

Scanner scanner = new Scanner(System.in);

String[] questions = {
        "What is your name?",
        "What is your quest?",
        "What is your favorite color?",
        "What is the capital of Assyria?",
        "What is the velocity of an unladen swallow?"
};

String[] cache = new String[questions.length];

BehaviorSubject<Integer> index = BehaviorSubject.create(0);

index
.observeOn(Schedulers.trampoline())
.concatMap(idx -> {
    if (idx == questions.length) {
        index.onCompleted();
        return Observable.empty();
    }

    System.out.println(questions[idx]);
    String answer = scanner.nextLine().trim();

    if ("BACK".equals(answer)) {
        index.onNext(Math.max(0, idx - 1));
        return Observable.empty();
    } else
    if ("QUIT".equals(answer)) {
        index.onCompleted();
        return Observable.empty();
    }

    cache[idx] = answer;
    index.onNext(idx + 1);
    return Observable.just(answer);
})
.subscribe(v -> System.out.println("You said: " + v));

答案 1 :(得分:0)

我认为如果Observable是有限的并且具有可管理的排放数量(如果每个都需要用户输入则应该是这种情况),有一种方法。您可以将其收集到toList()并在该列表中创建一个新的Observable。然后,您可以控制迭代并使用布尔标志随时发出倒带信号。

public class Test {

    public static void main(String[] args) {

        final Scanner scanner = new Scanner(System.in);
        final AtomicBoolean rewind = new AtomicBoolean();

        Observable.just(
                "What is your name?",
                "What is your quest?",
                "What is your favorite color?",
                "What is the capital of Assyria?",
                "What is the velocity of an unladen swallow?"
        ).toList().concatMap(l -> Observable.create(s -> {
            for (int i = 0; i < l.size(); i++) {
                s.onNext(l.get(i));
                if (rewind.getAndSet(false) &&  i >= 1) {
                    i = i - 2;
                }
            }
            s.onCompleted();
        }))
        .doOnNext(System.out::println)
        .concatMap(q -> Observable.create(s -> {
                    String answer = scanner.nextLine().trim();

                    if (answer.toUpperCase().equals("BACK")) {
                        rewind.set(true);
                    }
                    s.onNext(answer);
                    s.onCompleted();
                })
        ).filter(s -> !s.equals("BACK")).subscribe(q -> System.out.println("You said: " + q));

    }
}
What is your name?
Sir Lancelot of Camelot
You said: Sir Lancelot of Camelot
What is your quest?
To seek the Holy Grail
You said: To seek the Holy Grail
What is your favorite color?
Blue
You said: Blue
What is the capital of Assyria?
Asher
You said: Ashur
What is the velocity of an unladen swallow?
BACK
What is the capital of Assyria?