简体   繁体   English

如何使用一个可观察的状态跳过另一个可观察的值?

[英]How to use the state of one observable to skip values of another observable?

This is best explained with a short example. 最好用一个简短的例子来解释。
let's say this is my source observable that I want to filter 假设这是我要过滤的可观察到的源

Observable.interval(1, TimeUnit.SECONDS)

I use a checkbox to handle the filter state. 我使用复选框来处理过滤器状态。 When the box is not checked, I want to skip all values. 如果未选中该框,则我想跳过所有值。
I use RxAndroid to get an observable for the checkbox like this: 我使用RxAndroid获取可观察到的复选框,如下所示:

RxCompoundButton.checkedChanges(checkBox)

here is my code: 这是我的代码:

    Observable.combineLatest(
            RxCompoundButton.checkedChanges(checkBox)
            , Observable.interval(1, TimeUnit.SECONDS)
            , (isChecked, intervalCounter) -> {
                if (!isChecked) {
                    return null;
                } else {
                    return intervalCounter;
                }
            }
    ).filter(Objects::nonNull)

and I get the desired output 我得到所需的输出

interval       1--2--3--4--5--6--7--8--9  
checkbox       0-----1--------0-----1---
combineLatest
result         ------3--4--5--------8--9

I am just getting started with RxJava and my "solution" does not feel right, because: 我刚刚开始使用RxJava,我的“解决方案”感觉不正确,因为:
You can see that the combine function returns a magic value null and then the filter will skip these nulls . 您会看到Combine函数返回一个魔术值null ,然后过滤器将跳过这些nulls
That means that the filter function is called even though I already know, that I want to skip this data. 这意味着即使我已经知道,我也想跳过此数据,但调用了filter函数。

  • maybe I should use another operator 也许我应该使用另一个运算符
  • is there a way to use the checkbox-observable in a filter function 有没有办法在过滤器功能中使用复选框可观察
  • maybe there is some way in the combine function to signal that I want to skip this data 也许在合并功能中有某种方式可以表明我想跳过此数据

Your pictures of the desired output is not correct. 所需输出的图片不正确。 In your implementation combineLatest is combining a null into the stream : 在您的实现中, combineLatestnull合并到流中:

interval       1--2--3--4--5--6--7--8--9  
checkbox       0-----1--------0-----1---
combineLatest  N--N--3--4--5--N--N--8--9
filter(NonNull)------3--4--5--------8--9 

IMHO, using null as a signal is not good in Rx Stream, developers can easily fall into NullPointerException . 恕我直言,在Rx Stream中使用null作为信号不是很好,开发人员可以轻松地陷入NullPointerException

To avoid the use of null , there are two measures. 为了避免使用null ,有两种措施。 The first one is to transform the result to a Pair, and apply filter & map later. 第一个是将结果转换为对,然后再应用过滤器和映射。

A very simple Pair Class: 一个非常简单的Pair类:

public class Pair<T, U> {
    T first;
    U second;

    public Pair(T first, U second) {
        this.first = first;
        this.second = second;
    }
}

Then the whole implementation would like this: 然后整个实现将如下所示:

Observable.combineLatest(
        RxCompoundButton.checkedChanges(checkBox)
        , Observable.interval(1, TimeUnit.SECONDS)
        , (isChecked, intervalCounter) -> new Pair<>(isChecked,intervalCounter)
).filter(pair -> pair.first)
 .map(pair -> pair.second)  // optional, do this if you only need the second part

The data flow: 数据流:

interval          1      2      3      4
                  |      |      |      |
checkbox        F |  T   |   F  |  T   F
                  |  |   |   |  |  |   |
combineLatest    F1  T1  T2  F2 F3 T3  F4
                     |   |         |
filter(first=T)      T1  T2        T3
                     |   |         | 
map(second)          1   2         3

Or if you can Java 8 in your project, use Optional to avoid null , which is very similar to your solution but it gives the awareness for others developers that the Stream signal is Optional . 或者,如果您可以在项目中使用Java 8,则可以使用Optional来避免null ,这与您的解决方案非常相似,但是它使其他开发人员意识到Stream信号是Optional

Observable<Optional<Integer>> o1 = Observable.combineLatest(
        RxCompoundButton.checkedChanges(checkBox)
        , Observable.interval(1, TimeUnit.SECONDS)
        , (isChecked, intervalCounter) -> {
            if (!isChecked) {
                return Optional.empty();
            } else {
                return Optional.of(intervalCounter);
            }
        }
)

Observable<Integer> o2 = o1.filter(Optional::isPresent).map(Optional::get)

Some easier approach which is not same with combineLatest but identical to your desired result pictures. 一些更简单的方法,它与combineLatest但与您所需的结果图片相同。

// Approach 1
Observable.interval(1, TimeUnit.SECONDS).filter( i -> checkBox.isEnabled())

// Approach 2
Observable.interval(1, TimeUnit.SECONDS)
  .withLatestFrom(
    RxCompoundButton.checkedChanges(checkBox),
    (isChecked, intervalCounter) -> {
            if (!isChecked) {
                return Optional.empty();
            } else {
                return Optional.of(intervalCounter);
            }
     }).filter(Optional::isPresent).map(Optional::get)

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

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