我正在使用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。是他们的解决方案吗?
答案 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
的原因是您无法放弃对当前已解析的item
或rss