使用RxJava更新并保留现有数据

时间:2017-10-20 23:15:46

标签: rx-java reactive-programming rx-java2

我正在考虑将我的(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还不熟悉。

2 个答案:

答案 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