[英]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函数。
Your pictures of the desired output is not correct. 所需输出的图片不正确。 In your implementation combineLatest
is combining a null
into the stream : 在您的实现中, combineLatest
将null
合并到流中:
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.