我正在制作一个简单的天气应用来学习RxAndroid,我面临以下问题。
我首先加载我感兴趣的城市,然后询问每个城市的天气。
getCitiesUseCase 会返回我从数据库加载的Observable<List<City>>
。我将该城市列表发送到我的视图中以显示它们,然后在订户内单独询问天气(平面图)。
Subscription subscription = getCitiesUseCase.execute().flatMap(new Func1<List<City>, Observable<City>>() {
@Override
public Observable<City> call(List<City> cities) {
citiesView.addCities(cities);
return Observable.from(cities);
}
}).subscribe(new Subscriber<City>() {
@Override
public void onCompleted() {
subscriptions.remove(this);
this.unsubscribe();
}
@Override
public void onError(Throwable e) {
Log.e(this.getClass().getSimpleName(), e.toString());
}
@Override
public void onNext(City city) {
getCityWeatherUseCase.setLatLon(city.getLat().toString(), city.getLon().toString(), city.getId());
getCityWeather(city);
}
});
subscriptions.add(subscription);
现在,getCityWeather()方法如下所示:
private void getCityWeather(final City city) {
subscriptions.add(getCityWeatherUseCase.execute().subscribe(new Subscriber<CityWeather>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("error", e.toString());
}
@Override
public void onNext(CityWeather cityWeather) {
city.setCityWeather(cityWeather);
citiesView.updateCity(city);
}
}));
}
一切正常并且符合预期,但是我订阅了订阅者中的观察者这一事实并不合适。我知道rxJava可以让你玩订阅者来防止这种事情,但我真的不知道如何进一步改进我的代码。请记住,我需要一个城市才能要求天气。 圣诞快乐!
答案 0 :(得分:1)
一种方法可能如下。 (我正在使用retrolambda - 所以无论你在哪里看到->
,只需用新的匿名内部类替换。)
请注意,我正在使用flatMap
来调整天气数据请求,而不是像您的问题所暗示的Observable.concat
。这样做的原因是您的调度程序(例如io()
)将并行处理这些调度程序,并在结果可用时发送结果。但是,对于Observable.concat
,这些请求将被序列化,因此它们将被强制一次发生一次 - 使io()
之类的线程池的好处无效。
private class City {
public String name;
public City(String name) {
this.name = name;
}
public void setWeather(Weather weather) { /*...*/ }
}
private class Weather {
public String status;
public Weather(String status) {
this.status = status;
}
}
private Observable<Weather> getWeather(City city) {
// call your weather API here..
return Observable.just(new Weather("Sunny"));
}
@Test
public void test() {
Observable<List<City>> citiesObs = Observable.create(new Observable.OnSubscribe<List<City>>() {
@Override
public void call(Subscriber<? super List<City>> subscriber) {
// do work
final List<City> cities = new ArrayList<>();
cities.add(new City("Paris"));
cities.add(new City("Tokyo"));
cities.add(new City("Oslo"));
// send results
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(cities);
subscriber.onCompleted();
}
}
});
Observable<City> obs = citiesObs
// inject a side effect
.doOnNext(list -> {
// pass `list` to your view here
})
// turn Observable<Iterable<T>> into Observable<T>
.flatMapIterable(list -> list)
// Map a city to an observable that fetches Weather data
// Your scheduler can take care of these at once.
.flatMap(city -> {
return getWeather(city)
// another side effect
.doOnNext(weather -> {
city.setWeather(weather);
})
// map baack to city, just for the heck of it
.map($ -> city);
});
TestSubscriber sub = TestSubscriber.create();
obs.subscribe(sub);
sub.awaitTerminalEvent();
sub.assertValueCount(3);
}
另请注意,为了利用io()
,您需要添加对subscribeOn(Schedulers.io())
的调用,以告知observable开始在io
线程池上工作。如果要将控制权传递给另一个线程(例如视图),可以在副作用(或映射)之前插入observeOn(AndroidSchedulers.mainThread())
。如果您想将控制权反弹回天气电话的后台话题,则可以在observeOn(Schedulers.io())
到flatMap
之前添加另一个getWeather(City)
来电。