Wątki w Retrofit i RxJava

0

Ściągnąłem sobie chyba najprostszy przykład implementacji biblioteki Retrofit który krąży po internecie ( ten z pogodą z openweathermap).
Wszystko działa, ale chciałem zaimplementować tam RxJava do komunikacji pomiędzy warstwą UI oraz serwisami web, i pojawił mi się problem z kolejnością wykonywania działań z wątku asynchronicznego. Przykład przewija się bardzo często, ale mam kilka pytań odnośnie tego. Wstawię co najważniejsze, chociaż jeżeli ktoś pracuje z tym to pewnie dwa ostatnie paragrafy wystarczą do przedstawienia problemu. Wstawiłem kilka System.out z wypisaniem wątku który jest aktualnie używany żeby pokazać co się po kolei wykonuje.

Interfejs

public interface WeatherApi {
    @GET("/data/2.5/weather")
    Call<WeatherData> getWeatherFromApi (
            @Query("q") String cityName,
            @Query("APPID") String appId);
}

Subskrypcja z warstwy UI

    public void onButtonClick() {
        System.out.println("called onButtonClick, current thread is: " + Thread.currentThread());
        mWeatherAdapter.getWeatherObservable("Nazwa Miasta")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<WeatherData>() {
                    ...
                    @Override
                    public void onNext(WeatherData data) {
                        // obiekt data zwracany jest jako null, zanim zostanie zdefiniowany w żądaniu Retrofit
                        System.out.println("called onNext RxJava, current thread is: " + Thread.currentThread());
                        System.out.println("is data null?: " + data);
                    }
                });
    }

metoda wywołana z warstwy UI

    public Observable<WeatherData> getWeatherObservable(final String city) {
        return Observable.defer(new Func0<Observable<WeatherData>>() {
            @Override
            public Observable<WeatherData> call() {
                // wywołanie asynchronicznej metody która zwraca null object zamiast poczekać na jego inicjację
                return Observable.just(testWeatherApi(city));
            }
        });
    }

główny winowajca

    private WeatherData testWeatherApi(String city) {
        System.out.println("called testWeatherApi, current thread is: " + Thread.currentThread());
        mApi.getWeatherFromApi(city, Cons.OPEN_WEATHER_API).enqueue(new Callback<WeatherData>() {
            @Override
            public void onResponse(Call<WeatherData> call, Response<WeatherData> response) {
                System.out.println("called onResponse Retrofit, current thread is: " + Thread.currentThread());
                mData = response.body();
            }

            @Override
            public void onFailure(Call<WeatherData> call, Throwable t) {}
        });
        System.out.println("calling return mData, current thread is: " + Thread.currentThread());
        return mData;
    }

Po odpaleniu i kliknięciu, kolejność pokazana przez konsolę:
1 - called onButtonClick, current thread is: Thread[main,5,main]
2 - called testWeatherApi, current thread is: Thread[RxIoScheduler-2,5,main]
3 - calling return mData, current thread is: Thread[RxIoScheduler-2,5,main] ( obiekt zwrócony zanim doszło do wykonania zapytania web, obiekt data jest oczywiście null)
4 - called onNext RxJava, current thread is: Thread[main,5,main]
4.1 - is data null?: null
5 - called onResponse Retrofit, current thread is: Thread[main,5,main] zapełnienie obiektu "data" danymi ( to po tym powinien zostać zwrócony)

Teraz moje przemyślenia/pytania:

  1. Problem spowodowany zapewne przeskokiem pomiędzy wątkami, tzn. zapytanie na wątku Thread[RxIoScheduler-2,5,main] a odpowiedź zostanie nadana na wątku UI, tak więc wątek poboczny "nie czeka" na wykonanie się operacji ściągnięcia danych, tylko od razu przechodzi do zwrócenia niezainicjowanego obiektu data ( metoda private WeatherData testWeatherApi(String city))
  2. Dlaczego w ogóle "onResponse" z retofit jest na wątku UI zamiast pobocznym? Są jakieś niesamowite profity tego rozwiązania?
  3. Jak zabrać się za sklejenie tego z RxJava?

Jestem oczywiście świadom tego, że mógłbym zrobić sobie dodatkowy interfejs callback z jedną metodą ( np. void onResponseRetrofitCalled()) i za jego pomocą zwrócić obiekt "data" do warstwy UI, tylko, że stawia to pod zapytaniem całe stosowanie RxJava w tym przypadku ( poza tym, staram się ograniczać interfejsy callback po ostatnim projekcie).
Oczywiście mógłbym też przenieść to do innej warstwy, ale napotkałem fajny problem i chciałbym go rozwiązać.
Mam nadzieję, że ktoś się zainteresuje i wypowie, i rozwiązanie nie będzie trywialne:).

0
GregoryI napisał(a):
  1. Dlaczego w ogóle "onResponse" z retofit jest na wątku UI zamiast pobocznym? Są jakieś niesamowite profity tego rozwiązania?

onResponse jest w wątku UI, żeby można się było tam dostać do UI i napełnić komponenty odebranymi danymi. W przypadku używania RxJava nie korzystaj z onResponse.

GregoryI napisał(a):
  1. Jak zabrać się za sklejenie tego z RxJava?

Masz milion przykładów w internecie, np. wpisz sobie na githubie "retrofit rxjava" i zobacz jak to robią inni. Masz również różne blogi gdzie ludzie opisują jak się za to zabrać (znalezione w 5 sekund): http://randomdotnext.com/retrofit-rxjava/

0

Czemu tak dziwnie? Retrofit się pięknie integruje z RxJavą. Zamiast zwracać Call<WeatherData> zwróć Observable<WeatherData> prosto z serwisu retrofitowego.

0
bolson napisał(a):

Czemu tak dziwnie? Retrofit się pięknie integruje z RxJavą. Zamiast zwracać Call<WeatherData> zwróć Observable<WeatherData> prosto z serwisu retrofitowego.

Tak postanowiłem, ale doszły różne wersje Retrofita i RxJavy, i mam co robić na dzisiaj z ogarnianiem:)

1 użytkowników online, w tym zalogowanych: 0, gości: 1