RxAnadroid连接流

时间:2015-12-24 09:10:50

标签: android rx-java rx-android

我正在制作一个简单的天气应用来学习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可以让你玩订阅者来防止这种事情,但我真的不知道如何进一步改进我的代码。请记住,我需要一个城市才能要求天气。 圣诞快乐!

1 个答案:

答案 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)来电。