How Retrieve Response Body with RxAndroid and Retrofit 2?

10,970

Try something like this:

import okhttp3.ResponseBody;
import retrofit2.Response;

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

Edit: Don't forget you have to specify the adapter for RxJava:

    new Retrofit.Builder()
            ...
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build()
            .create(Api.class);
Share:
10,970
Bryan
Author by

Bryan

Professional software engineer and game development hobbyist.

Updated on July 24, 2022

Comments

  • Bryan
    Bryan almost 2 years

    I am using Retrofit 2 (beta 4), and I was looking to move from using the standard Call response to the RxAndroid Observable response. I was successful in switching most of my calls with a simple swap from Call<List<ExampleObject>> to Observable<List<ExampleObject>>. A few of my calls use Call<okhttp3.ResponseBody>, which works great, but when I swapped out Call, I was met with an error:

    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
    

    The reason I am using the ResponseBody instead of another object as usual, is because in these cases I need to parse HTML, and as far as I know, there is no Retrofit converter for an HTML parser. I know I probably could create one on my own, but I would rather not for the small amount of HTML I have to parse.

    My question is why doesn't the Retrofit 2 RxJava Adapter support ResponseBody when Retrofit 2 itself does? Is there another way I can obtain the response string from an Observable?


    My Service:

    public interface AuthenticationService() {
    
        @GET("cas/login")
        Observable<Response<ResponseBody>> login();
    }
    

    Relevant Retrofit code:

    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);
    }
    

    Response attempt:

    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");
        }
    }
    

    New stack trace:

    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
    
    • Tudor Luca
      Tudor Luca about 8 years
      Please post the actual code.
    • Bryan
      Bryan about 8 years
      @TudorLuca Updated with code
    • Bryan
      Bryan about 8 years
      @TudorLuca I'm sorry for wasting your time. I found out that I actually was using a Retrofit object (that I should've deleted) that didn't have an RxJavaCallAdapterFactory. I am tearing apart the current code, and it's a bit of a mess. Thank you for you help anyway.
    • tir38
      tir38 almost 8 years
      @Bryan can you clarify that last statement?
    • Bryan
      Bryan almost 8 years
      @tir38 It was a dumb mistake, at the time I was trying to fix some old code. I had accidentally set up two Retrofit objects within my code, one with the RxJavaCallAdapterFactory set up and one without it. I querying the old Retrofit object without the adapter factory, which is why I couldn't get an Observable<ResponseBody>.
  • Bryan
    Bryan about 8 years
    Tried that, same result :/ I also tried a plain okhttp3.Respone, that doesn't work either.
  • Bryan
    Bryan about 8 years
    Yes, I have the RxJavaCallAdapterFactory set up correctly, as my other Observable<Example> responses work as expected.
  • Tudor Luca
    Tudor Luca about 8 years
    It won't work with okhttp3.Respone, you need Retrofit's Response.
  • Bryan
    Bryan about 8 years
    I had been using retrofit2.Response, along with okhttp3.ResponseBody (okHttp3 doesn't have a Response<T>, just a Response). Have you gotten this to work in the past?
  • Tudor Luca
    Tudor Luca about 8 years
    My answer should work just fine. Post the entire code you're using, and the stacktrace you get with my code.
  • Bryan
    Bryan about 8 years
    It also works with just the Observable<ResponseBody>, without the need for Response<T>.