如何使用 RxAndroid 和 Retrofit 2 检索响应主体?

How Retrieve Response Body with RxAndroid and 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.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(LoginFragment.java:168)
            at com.example.app.ui.fragment.LoginFragment.access$lambda(LoginFragment.java)
            at com.example.app.ui.fragment.LoginFragment$$Lambda.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.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(LoginFragment.java:160)
            at com.example.app.ui.fragment.LoginFragment.access$lambda(LoginFragment.java)
            at com.example.app.ui.fragment.LoginFragment$$Lambda.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

尝试这样的事情:

import okhttp3.ResponseBody;
import retrofit2.Response;

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

编辑:别忘了你必须为 RxJava 指定适配器:

    new Retrofit.Builder()
            ...
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build()
            .create(Api.class);