Как получить тело ответа с помощью RxAndroid и Retrofit 2?

Я использую Retrofit 2 (бета-версия 4) и хотел перейти от стандартного ответа Call к ответу RxAndroid Observable. Мне удалось переключить большинство моих вызовов простым переключением с Call<List<ExampleObject>> на Observable<List<ExampleObject>>. В некоторых моих вызовах используется Call<okhttp3.ResponseBody>, что прекрасно работает, но когда я заменил Call, я столкнулся с ошибкой:

03-03 15:21:44.237 27333-27333/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.app, PID: 27333
        java.lang.IllegalArgumentException: Unable to create call adapter for rx.Observable<okhttp3.ResponseBody>
            for method AuthenticationService.getLoginForm
            at retrofit2.Utils.methodError(Utils.java:119)
            at retrofit2.MethodHandler.createCallAdapter(MethodHandler.java:52)
            at retrofit2.MethodHandler.create(MethodHandler.java:25)
            at retrofit2.Retrofit.loadMethodHandler(Retrofit.java:164)
            at retrofit2.Retrofit$1.invoke(Retrofit.java:145)
            at java.lang.reflect.Proxy.invoke(Proxy.java:393)
            at $Proxy6.getLoginForm(Unknown Source)
            at com.example.app.ui.fragment.LoginFragment.login(LoginFragment.java:214)
            at com.example.app.ui.fragment.LoginFragment.lambda$onContinue$1(LoginFragment.java:168)
            at com.example.app.ui.fragment.LoginFragment.access$lambda$1(LoginFragment.java)
            at com.example.app.ui.fragment.LoginFragment$$Lambda$4.onClick(Unknown Source)
            at android.view.View.performClick(View.java:5204)
            at android.view.View$PerformClick.run(View.java:21153)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:148)
            at android.app.ActivityThread.main(ActivityThread.java:5417)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
        Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for rx.Observable<okhttp3.ResponseBody>.
         Tried:
          * retrofit2.ExecutorCallAdapterFactory
           at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:230)
           at retrofit2.Retrofit.callAdapter(Retrofit.java:194)
           at retrofit2.MethodHandler.createCallAdapter(MethodHandler.java:50)
              ... 18 more

Причина, по которой я использую ResponseBody вместо другого объекта, как обычно, заключается в том, что в этих случаях мне нужно анализировать HTML, и, насколько я знаю, нет конвертера Retrofit для парсера HTML. Я знаю, что, вероятно, мог бы создать его самостоятельно, но я бы не стал этого делать из-за небольшого количества HTML, который мне нужно разобрать.

Мой вопрос: почему адаптер Retrofit 2 RxJava не поддерживает ResponseBody, когда сам Retrofit 2 поддерживает? Есть ли другой способ получить строку ответа от Observable?


Мой сервис:

public interface AuthenticationService() {

    @GET("cas/login")
    Observable<Response<ResponseBody>> login();
}

Соответствующий код модернизации:

public static Retrofit getRetrofit() {
    if(mRetrofit == null) {
        return new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(getGson()))
                .client(getOkHttpClient())
                .build();
    } return mRetrofit;
}

public static AuthenticationService getAuthenticationService() {
    return getRetrofit().create(AuthenticationService.class);
}

Попытка ответа:

private void login() {
    RestClient.getAuthenticationService().login()
            .observeOn(ASchedulers.newThread())
            .subscribeOn(AndroidSchedulers.mainThread())
            .doOnNext(this::onLoginResponse);
}

private void onLoginResponse(Response<ResponseBody>> response) {
    try {
        parseResponse(response.body().string());
    } catch (IOException) {
        Timber.w(throwable, "Failed to login");
    }
}

Новая трассировка стека:

03-03 16:14:57.848 26866-26866/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.app, PID: 26866
        java.lang.IllegalArgumentException: Unable to create call adapter for rx.Observable<retrofit2.Response<okhttp3.ResponseBody>>
            for method AuthenticationService.getLoginForm
            at retrofit2.Utils.methodError(Utils.java:119)
            at retrofit2.MethodHandler.createCallAdapter(MethodHandler.java:52)
            at retrofit2.MethodHandler.create(MethodHandler.java:25)
            at retrofit2.Retrofit.loadMethodHandler(Retrofit.java:164)
            at retrofit2.Retrofit$1.invoke(Retrofit.java:145)
            at java.lang.reflect.Proxy.invoke(Proxy.java:393)
            at $Proxy3.getLoginForm(Unknown Source)
            at com.example.app.ui.fragment.LoginFragment.login(LoginFragment.java:206)
            at com.example.app.ui.fragment.LoginFragment.lambda$onContinue$1(LoginFragment.java:160)
            at com.example.app.ui.fragment.LoginFragment.access$lambda$1(LoginFragment.java)
            at com.example.app.ui.fragment.LoginFragment$$Lambda$4.onClick(Unknown Source)
            at android.view.View.performClick(View.java:5204)
            at android.view.View$PerformClick.run(View.java:21153)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:148)
            at android.app.ActivityThread.main(ActivityThread.java:5417)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
        Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for rx.Observable<retrofit2.Response<okhttp3.ResponseBody>>.
         Tried:
          * retrofit2.ExecutorCallAdapterFactory
           at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:230)
           at retrofit2.Retrofit.callAdapter(Retrofit.java:194)
           at retrofit2.MethodHandler.createCallAdapter(MethodHandler.java:50)
              ... 18 more

person Bryan    schedule 03.03.2016    source источник
comment
Пожалуйста, опубликуйте актуальный код.   -  person Tudor Luca    schedule 04.03.2016
comment
@TudorLuca Обновлено с кодом   -  person Bryan    schedule 04.03.2016
comment
@TudorLuca Прошу прощения за потраченное впустую время. Я обнаружил, что на самом деле использовал объект Retrofit (который я должен был удалить), у которого не было RxJavaCallAdapterFactory. Я разрываю текущий код, и это немного беспорядок. Спасибо за помощь в любом случае.   -  person Bryan    schedule 08.03.2016
comment
@ Брайан, можешь пояснить последнее утверждение?   -  person tir38    schedule 08.06.2016
comment
@ tir38 Это была глупая ошибка, когда я пытался исправить какой-то старый код. Я случайно настроил в своем коде два объекта Retrofit, один с настроенным RxJavaCallAdapterFactory, а другой без него. Я запрашиваю старый объект Retrofit без фабрики адаптеров, поэтому не могу получить объект Observable<ResponseBody>.   -  person Bryan    schedule 08.06.2016


Ответы (1)


Попробуйте что-то вроде этого:

import okhttp3.ResponseBody;
import retrofit2.Response;

@GET("/whatever")
Observable<Response<ResponseBody>> getWhatever();

Изменить: не забудьте указать адаптер для RxJava:

    new Retrofit.Builder()
            ...
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build()
            .create(Api.class);
person Tudor Luca    schedule 03.03.2016
comment
Пробовал, тот же результат :/ Я также пробовал обычный okhttp3.Respone, он тоже не работает. - person Bryan; 03.03.2016
comment
Да, я правильно настроил RxJavaCallAdapterFactory, так как другие мои ответы Observable<Example> работают как положено. - person Bryan; 03.03.2016
comment
Это не будет работать с okhttp3.Respone, вам нужен ответ Retrofit. - person Tudor Luca; 04.03.2016
comment
Я использовал retrofit2.Response вместе с okhttp3.ResponseBody (у okHttp3 нет Response<T>, только Response). Вы заставляли это работать в прошлом? - person Bryan; 04.03.2016
comment
Мой ответ должен работать нормально. Опубликуйте весь код, который вы используете, и трассировку стека, которую вы получаете с моим кодом. - person Tudor Luca; 04.03.2016
comment
Он также работает только с Observable<ResponseBody>, без необходимости Response<T>. - person Bryan; 08.03.2016