RxJava flatmap链接请求

时间:2016-10-19 13:24:11

标签: android retrofit rx-java chaining flatmap

我正在使用RxJAva的Retrofit作为获取Rss Feeds的应用程序,但rss不包含所有信息,所以我使用jsoup来解析每个项目链接,检索图像和文章的描述。现在我用这种方式:

public Observable<Rss> getDumpData() {
    return newsAppService.getDumpData()
            .flatMap(rss -> Observable.from(rss.channel.items)
            .observeOn(Schedulers.io())
            .flatMap(Checked.f1(item -> Observable.just(Jsoup.connect(item.link).get())
            .observeOn(Schedulers.io())
            .map(document -> document.select("div[itemprop=image] > img").first())
                    .doOnNext(element -> item.image = element.attr("src"))
            )))
            .defaultIfEmpty(rss)
            .ignoreElements()
            .observeOn(Schedulers.io())
            .subscribeOn(AndroidSchedulers.mainThread());
}

我在这一行收到错误:defaultIfEmpty(rss) 它无法识别flatmap的rss。当我在flatmap括号中移动defaultIfEmpty(rss)时,我有另一个错误,说必须将返回类型更改为Element。是他们的解决方案吗?

2 个答案:

答案 0 :(得分:2)

首先,您需要使用observeOn去掉所有并发并使用subscribeOn。

.observeOn(Schedulers.io())

如果想将来自另一个线程的数据同步回事件循环,请考虑将observeOn与AndroidScheduler一起使用。通常你会在订阅一个observable之前使用observeOn来同步回ui-loop并改变ui-information。

.observeOn(AndroidSchedulers.mainThread())

其次,不建议改变管道中的对象。你应该及时返回一个新对象。

.doOnNext(element -> item.image = element.attr("src"))

在考虑前两点的情况下,我试图重构你的解决方案。我正在使用RxJava2-RC5

flatMap运算符有很多overloades。其中一个提供了将传入值和创建值压缩在一起的功能。

Observable<Rss> rssItemObservable = newsService.getDumpData()
                .flatMap(rss -> getRssItemInformation(rss).subscribeOn(Schedulers.io()),
                        (r, rItemList) -> {
                            Rss rInterim = new Rss();
                            rInterim.items = rItemList;
                            return rInterim;
                        });

用于检索Rss中每个项目的信息的帮助方法。请考虑使用maxConcurrency的重载,因为默认情况下它会立即订阅每个流。因此flatMap会创建许多http请求。

private Observable<List<RssItem>> getRssItemInformation(Rss rss) {
        return Observable.fromIterable(rss.items)
                .flatMap(rssItem -> getImageUrl(rssItem).subscribeOn(Schedulers.io()), (rItem, img) -> {
                    RssItem item = new RssItem();
                    printCurrentThread("merge1");
                    item.image = img;
                    item.link = rItem.link;
                    return item;
                }).toList().toObservable();
}

用于检索图像URL的帮助方法。返回可观察性并不是关于并发性的。如果发生错误,则返回空字符串作为默认值。

private Observable<String> getImageUrl(String link) {
           return Observable.fromCallable(() -> Jsoup.connect(link).get())
                .map(document -> document.select("div[itemprop=image] > img").first())
                .map(element -> element.attr("src"))
                .onErrorResumeNext(throwable -> {
                    return Observable.just("");
                });
}

您可以在github.gist上查看完整示例:https://gist.github.com/anonymous/a8e36205fc2430517c66c802f6eef38e

答案 1 :(得分:1)

您不能将一个RxJava参数(flatMap lambda参数)的内部参数与另一个运算符参数(defaultIfEmpty)混合。

首先,创建一个辅助函数来保持主反应流清洁:

private Observable<List<Item>> getDetails(List<Item> items) {
    return Observable.from(items)
               .observeOn(Schedulers.io())
               .flatMap(Checked.f1(item ->
                   Observable.zip(
                       Observable.just(item),
                            Observable.just(Jsoup.connect(item.link).get())
                           .observeOn(Schedulers.io())
                           .map(document -> document.select("div[itemprop=image] > img").first()),
                           (itemInner, element) -> {
                                itemInner.image = element.attr("src");
                                return itemInner;
                           }
                   )
               ))
               .toList();
}

然后重新格式化主要功能:

newsAppService.getDumpData()
    .flatMap(rss ->
        Observable.zip(
            Observable.<Rss>just(rss),
            getDetails(rss.channel.items),
            (rssInner, items) -> {
                rssInner.channel.items = items;
                return rss;
            }).onErrorResumeNext((throwable -> Observable.just(rss))
        )
    )
    .observeOn(Schedulers.io())
    .subscribeOn(AndroidSchedulers.mainThread());

希望我的目标得当。它可能无法正常工作,因为我无法测试它,但我希望你能得到这个想法。我使用.zip的原因是您无法放弃对当前已解析的itemrss

的引用