我正在考虑将我的(Android)应用程序中的一些逻辑转换为使用RxJava,但是我很难想出一种方法来做更高级的逻辑。
用例如下:我想向用户显示一种Feed。 Feed由来自不同来源的商品组成,例如消息,文章等。由于API限制,应用程序本身必须收集各个来源并将其显示在一个列表中。
例如,假设Feed中的项目如下:
class FeedItem {
Type feedItem; // Type of the item, e.g. article, message, etc.
...
}
目前,Feed是在单独的线程上构建的,并且在更新Feed时使用侦听器通知UI。为了让您了解它是如何完成的,下面是一些(伪)Java代码(为清晰起见,省略了线程和其他管理代码)。
class FeedProducer {
List<FeedItem> currentData = new ArrayList();
public void refreshData() {
for (FeedSource source: getFeedSources()) {
Type sourceType = source.getType();
// Remove existing items
currentData.removeIf(item -> item.feedItem.equals(sourceType));
List<FeedItem> newItems = source.produceItems();
// Add the new items
currentData.addAll(newItems);
// Notify the UI things have changed
notifyDataChanged(currentData);
}
// Notify the UI we are done loading
notifyLoadingComplete();
}
}
每次用户想要刷新数据时,都会调用此方法refreshData()
。这样,可以仅更新某些来源,而其他来源保持不变(例如,通过更改getFeedSources()
的返回值)。
这些来源也可以在应用的其他部分单独使用;我已将它们转换为Observables。这使事情变得更容易,例如如果数据库发生变化,Observable会将更改简单地推送到UI。
因此,我的问题是如何(优雅地)将这些Observable源合并到一个Observable中,但是哪里有先前结果的“全局”状态。我已经调查了各种组合运算符,但还没找到我需要的东西。如果我忽视了一些显而易见的事情,我很抱歉,因为我对RxJava还不熟悉。
答案 0 :(得分:1)
如果您有3个返回[0, 1]
,[10, 11]
和[20, 21]
的单个任务,则需要将它们合并到一个列表中。在这种情况下,您可以使用zip
操作。
public class TestRx {
public static void main(String[] args) {
// some individual observables.
Observable<List<Integer>> observable1 = Observable.just(Arrays.asList(0, 1));
Observable<List<Integer>> observable2 = Observable.just(Arrays.asList(10, 11));
Observable<List<Integer>> observable3 = Observable.just(Arrays.asList(20, 21));
Observable.zip(observable1, observable2, observable3,
new Func3<List<Integer>, List<Integer>, List<Integer>, List<Integer>>() {
@Override
public List<Integer> call(List<Integer> list1, List<Integer> list2, List<Integer> list3) {
// TODO: Remove existing items
// merge all lists
List<Integer> mergedList = new ArrayList<>();
mergedList.addAll(list1);
mergedList.addAll(list2);
mergedList.addAll(list3);
return mergedList;
}
})
.subscribe(new Observer<List<Integer>>() {
@Override
public void onNext(List<Integer> mergedList) {
System.out.println(mergedList);
// TODO: notifyDataChanged(mergedList)
}
@Override
public void onError(Throwable throwable) {
System.out.println(throwable.toString());
// TODO: handle exceptions
}
@Override
public void onCompleted() {
// TODO: notifyLoadingComplete()
}
});
}
}
因此,它会像[0, 1, 10, 11, 20, 21]
一样打印。
答案 1 :(得分:1)
Naive选项是,调用者存储最后一个列表并在您请求新数据时将其作为参数提供:
public class ReactiveMultipleSources {
// region Classes
public enum SourceType {
TYPE_ARTICLE,
TYPE_MESSAGE,
TYPE_VIDEO
}
public static class Feed {
private SourceType sourceType;
private String content;
Feed(SourceType sourceType, String content) {
this.sourceType = sourceType;
this.content = content;
}
SourceType getSourceType() {
return sourceType;
}
}
// endregion
public static void main(String[] args) throws InterruptedException {
final List<Feed>[] currentList = new List[]{new ArrayList()};
// Simulate refresh
refreshContent(currentList[0])
.subscribe(feeds -> {
currentList[0] = feeds;
for (int i = 0; i < currentList[0].size(); i++) {
System.out.println(currentList[0].get(i).content);
}
});
Thread.sleep(2000);
System.out.println();
// Simulate refresh
refreshContent(currentList[0])
.subscribe(feeds -> {
currentList[0] = feeds;
for (int i = 0; i < currentList[0].size(); i++) {
System.out.println(currentList[0].get(i).content);
}
});
Thread.sleep(2000);
}
private static Observable<List<Feed>> refreshContent(@NotNull List<Feed> currentFeed) {
return Observable.fromIterable(getSourceTypes())
.observeOn(Schedulers.io())
// Get List<Feed> forEach sourceType
.concatMap(ReactiveMultipleSources::getFeedItemsBySourceType)
.observeOn(Schedulers.computation())
// Get list of "List of Feed for sourceType", = List<List<Feed>>
.toList()
.map(lists -> {
for (List<Feed> list : lists) {
SourceType sourceType = list.get(0).getSourceType();
// Remove items of currentFeed whose sourceType has new List<Feed>
currentFeed.removeIf(temp -> temp.getSourceType() == sourceType);
// Add new items
currentFeed.addAll(list);
}
return currentFeed;
})
.toObservable();
}
// region Helper
private static List<SourceType> getSourceTypes() {
return new ArrayList<>(Arrays.asList(SourceType.values()));
}
private static Observable<List<Feed>> getFeedItemsBySourceType(SourceType sourceType) {
String content;
if (sourceType == SourceType.TYPE_ARTICLE)
content = "article ";
else if (sourceType == SourceType.TYPE_MESSAGE)
content = "message ";
else if (sourceType == SourceType.TYPE_VIDEO)
content = "video ";
else
content = "article ";
Feed feed1 = new Feed(sourceType, content + createRandomInt());
Feed feed2 = new Feed(sourceType, content + createRandomInt());
Feed feed3 = new Feed(sourceType, content + createRandomInt());
Feed feed4 = new Feed(sourceType, content + createRandomInt());
return Observable.just(Arrays.asList(feed1, feed2, feed3, feed4));
}
// For simulating different items each time List<Feed> is required
private static int createRandomInt() {
return ThreadLocalRandom.current().nextInt(0, 21);
}
// endregion
}
示例输出:
article 19
article 15
article 18
article 18
message 3
message 2
message 9
message 1
video 19
video 17
video 18
video 11
article 0
article 4
article 18
article 15
message 11
message 16
message 16
message 4
video 1
video 7
video 20
video 2