网络请求(三)— Retrofit

1 Retrofit简介

A type-safe HTTP client for Android and Java

Retrofit是一个网络请求框架的封装,底层网络请求默认使用OkHttp,也就是说Retrofit本身并不具备网络请求的能力,只是简化了网络请求的相关参数配置或者说Retrofit只负责上层接口的封装。

使用Retrofit,实际上是使用Retrofit接口层封装请求参数、HeaderUrl 等信息,之后由OkHttp完成后续的请求操作,当服务端返回数据之后,OkHttp再将原始的结果交给Retrofit,然后Retrofit根据用户的需求,对结果进行解析。

Retrofit不仅具备了OkHttp的高效性,还有以下优势:

  • 支持RESTful API设计风格;
  • 通过注解配置请求:包括请求方式、请求参数、请求头、返回值等
  • 可以搭配多种Converter(转换器)将获得的数据自动解析和序列化:支持GsonJacksonProtoful
  • 提供了对RxJava的支持

converter [kənˈvɜːrtər] 转换器;整流器;变频器;转换程序;变焦镜

RESTful API

  • REST (Respresentational State Transfer),表述性状态转移;
  • REST只是风格而没有标准,其核心是一切皆资源。这里的资源不一定只是数据,而是数据加表现形式的组合。任何可命名的抽象概念都可以定义为一个资源;
  • 符合REST风格的API就可以叫做RESTful API

比如说,如果要访问https://xxxx/user/10000/info,对应的找到userId10000的这条用户的数据:

// GET、POST、PUT、DELETE
@GET("/user/{userId}/info")
Call<UserInfo> getUserInfo(@Path("userId") String userId)

RESTful对资源操作:

  • POST:用于在服务器中新建一个资源,对应资源操作是INSERT,非幂等且不安全;
  • DELETE:用于从服务器删除资源,对饮的资源是DELETE,幂等且不安全;
  • PUT:用于在服务器中更新资源,客户端提供改变后的完整资源,对应资源操作是UPDATE,幂等且不安全;
  • GET:用于从服务器中取出资源,对应资源操作是SELETE,幂等且安全;

2 Retrofit的设计思想

Retrofit的设计基于以下几个事实:同一款应用中的网络请求绝大多数指向的是同一个服务器域名。因为在大多数的情况下,客户端和服务器的数据都是配套使用的。另外,服务器提供的接口通常是可以根据功能来归类的。比如新增用户、修改用户数据、查询用户数据这几个接口就可以归为一类,上架新书、销售图书、查询可供销售图书这几个接口也可以归为一类。将服务器接口合理归类能够让代码结构变得更加合理,从而提高可阅读性和可维护性。最后,开发者肯定更加习惯于“调用一个接口,获取它的返回值”这样的编码方式,大多数人并不关心网络的具体通信细节,但是传统网络库的用法却需要编写太多网络相关的代码。

Retrofit的用法就是基于以上几点来设计的,首先配置好一个根路径,然后在指定服务器接口地址时只需要使用相对路径即可,这样就不用每次都指定完整的URL地址了。另外,Retrofit允许我们对服务器接口进行归类,将功能同属一类的服务器接口定义到同一个接口文件当中,从而让代码结构变得更加合理。

最后,我们也完全不用关心网络通信的细节,只需要在接口文件中声明一系列方法和返回值, 然后通过注解的方式指定该方法对应哪个服务器接口,以及需要提供哪些参数。当我们在程序中调用该方法时,Retrofit会自动向对应的服务器接口发起请求,并将响应的数据解析成返回值声明的类型。这就使得我们可以用更加面向对象的思维来进行网络操作。

3 Retrofit的基本用法

想使用Retrofit,需要先在项目中添加必要的依赖库。在app/build.gradle文件中添加如下内容:

dependencies {
    ...
    implementation 'com.squareup.retrofit2:retrofit:2.6.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
}

由于Retrofit是基于OkHttp开发的,因此添加第一条依赖时,会自动将RetrofitOkHttpOkio这几个库一起下载,无须再手动引入OkHttp库。另外,Retrofit还会将服务器返回的JSON数据自动解析成对象,因此上述第二条依赖就是一个Retrofit的转换库,它是借助GSON来解析GSON数据的,所以会自动将GSON库一起下载下来,这样我们也不用手动引入GSON库 了。除了GSON之外,Retrofit还支持各种其他主流的JSON解析库,包括JacksonMoshi等, 不过毫无疑问GSON是最常用的。

由于Retrofit会借助converter-gsonJSON数据转换成对象,因此需要定义一个User类,如下所示:

class User(val id: Int, val username: String, val mobile: String) 

接下来,根据服务器接口的功能进行归类,创建不同种类的接口文件,并在其中定义对应具体服务器接口的方法。新建ApiService接口,代码如下所示:

interface ApiService {

  @FormUrlEncoded
  @POST("login")
  fun login(@Field("username") username: String, @Field("password") password: String)
  : Call<ResponseBody>

}

通常Retrofit的接口文件建议以具体的功能种类名开头,并以Service结尾,这是一种比较好的命名习惯。

上述代码中有两点需要注意。第一就是在login方法上面添加的注解,这里使用 了一个@POST@FormUrlEncoded注解,表示当调用login方法时Retrofit会发起一条POST请求,请求的地址就是在@POST注解中传入的具体参数。注意,这里只需要传入请求地址的相对路径即可,根路径会在稍后设置。

第二就是login方法的返回值必须声明成Retrofit中内置的Call类型,并通过泛型来指定服务器响应的数据应该转换成什么对象。由于服务器响应的是一个包含ResponseBody数据的JSON数据,因此这里将泛型声明成ResponseBody。当然,Retrofit还提供了强大的Call Adapters功能来允许我们自定义方法返回值的类型,比如Retrofit结合RxJava使用就可以将返回值声明成ObservableFlowable等类型。

定义好了ApiService接口之后,接下来的问题就是该如何使用它:

val retrofit = Retrofit.Builder()
		.baseUrl(BASE_URL)
		.addConverterFactory(GsonConverterFactory.create())
		.build()

val apiService = retrofit.create(ApiService::class.java)

apiService.login("admin", "1123456").enqueue(object : retrofit2.Callback<ResponseBody> {
  override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
    val result = response.body()?.string()
  }

  override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
    t.printStackTrace()
  }

})

首先使用了Retrofit.Builder来构建 一个Retrofit对象,其中baseUrl方法用于指定所有Retrofit请求的根路径, addConverterFactory()方法用于指定Retrofit在解析数据时所使用的转换库,这里指定成GsonConverterFactory。注意这两个方法都是必须调用的。

有了Retrofit对象之后,就可以调用它的create()方法,并传入具体Service接口所对应的Class类型,创建一个该接口的动态代理对象。有了动态代理对象之后,就可以随意调用接口中定义的所有方法,而 Retrofit会自动执行具体的处理就可以了。

对应到上述的代码当中,当调用了ApiServicelogin方法时,会返回一个 Call<ResponseBody>对象,这时再调用一下它的enqueue()方法,Retrofit就会根据注解中配置的服务器接口地址去进行网络请求了,服务器响应的数据会回调到enqueue()方法中传 入的Callback实现里面。需要注意的是,当发起请求的时候,Retrofit会自动在内部开启子线程,当数据回调到Callback中之后,Retrofit又会自动切换回主线程,整个操作过程中我们都不用考虑线程切换问题。在CallbackonResponse()方法中,调用response.body()方法将会得到Retrofit解析后的对象,也就是ResponseBody类型的数据。

Retrofit的基本使用流程:

  • 通过建造者模式构建一个Retrofit实例;
  • 通过Retrofit对象的create方法创建接口实例(动态代理);
  • 调用接口的方法解析注解,调用具体的网络请求方法;
  • 通过数据解析器解析数据;
  • 调用回调执行器,切换线程,处理返回结果;

动态代理:运行时创建的代理类,在委托类的方法前后去做一些事情。在运行过程中,会在虚拟机内部创建一个Proxy的类。通过实现InvocationHandler的接口,来代理委托类的函数。使用动态代理来对接口中的注释进行解析,解析后完成OkHttp的参数构建。这样做的好处是代理类原始类脱离联系,在原始类和接口未知的时候就确定代理类的行为。 以下是相关源码:

public final class Retrofit {
  public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)
      Proxy.newProxyInstance(
      service.getClassLoader(),
      new Class<?>[] {service},
      new InvocationHandler() {
        private final Platform platform = Platform.get();
        private final Object[] emptyArgs = new Object[0];

        @Override
        public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
          throws Throwable {
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
          args = args != null ? args : emptyArgs;
          return platform.isDefaultMethod(method)
            ? platform.invokeDefaultMethod(method, service, proxy, args)
            : loadServiceMethod(method).invoke(args);
        }
      });
  }
}

Retrofit的基本使用流程很简洁,其内部使用了优秀的架构设计和大量的设计模式。比如,Retrofit构建过程会用到建造者模式、工厂方法模式,建网络请求接口实例过程会用到外观模式、代理模式、单例模式、策略模式、装饰模式(建造者模式),生成并执行请求过程适配器模式(代理模式、装饰模式)。

4 Retrofit构造器的最佳写法

学到这里,有一个问题就是获取Service接口的动态代理对象实在是太麻烦了。先回顾一下之前的写法,大致代码如下所示:

val retrofit = Retrofit.Builder()
		.baseUrl(BASE_URL)
		.addConverterFactory(GsonConverterFactory.create())
		.build()

想要得到ApiService的动态代理对象,需要先使用Retrofit.Builder构建出一个Retrofit对象,然后再调用Retrofit对象的create()方法创建动态代理对象。如果只是写一次还好,每次调用任何服务器接口时都要这样写一遍的话,是很繁琐的。

事实上,确实没有每次都写一遍的必要,因为构建出的Retrofit对象是全局通用的,只需要在调用create()方法时针对不同的Service接口传入相应的Class类型即可。因此,可以将通用的这部分功能封装起来,从而简化获取Service接口动态代理对象的过程。

新建一个ServiceCreator单例类,代码如下所示:

object ServiceCreator {

    private val BASE_URL = "http://10.0.2.2"

    private val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)
}

这里使用object关键字让ServiceCreator成为了一个单例类,并在它的内部定义了一 个BASE_URL常量,用于指定Retrofit的根路径。然后同样是在内部使用Retrofit.Builder构建一个Retrofit对象,注意这些都是用private修饰符来声明的,相当于对于外部而言它们都是不可见的。

最后,提供了一个外部可见的create()方法,并接收一个Class类型的参数。当在外部调用这个方法时,实际上就是调用了Retrofit对象的create()方法,从而创建出相应Service接口的动态代理对象。

经过这样的封装之后,Retrofit的用法将会变得异常简单,比如我们想获取一个Appiervice接口的动态代理对象,只需要使用如下写法即可:

val apiService = ServiceCreator.create(ApiService::class.java)

之后就可以随意调用ApiService接口中定义的任何方法了。

5 源码分析

Retrofit使用建造者模式来创建实例,之后在Retrofit.create方法中使用动态代理技术创建接口(ApiService)的实现类,将接口(ApiServie)中的方法调用集中到InvocationHandler.invoke方法中。

建造者模式主要是用来构建灵活性和扩张性较强的复杂对象。

代理模式是通过代理对象访问目标对象,这样做的好处是可以通过代理对象扩展目标对象的功能。静态代理在程序运行前就已经将代理的源码编译成字节码文件,代理类和目标类的关系在运行前就已经确定了。动态代理是在程序运行期间由JVM根据反射原理等机制动态生成的,所以不会有代理类的字节码文件,代理类和目标类的关系是在程序运行时确定的。动态代理的好处就是更加灵活,更适用于处理目标对象不固定的情况(接口不固定,当作参数传入)。

Java的反射机制是在程序运行期间,对于任意一个类,都知道这个类的属性和方法,对于任意一个对象都能都能够调用它的属性和方法。

InvocationHandler.invoke方法中调用了Retrofit.loadServiceMethod(method)方法来获取ServiceMethodServiceMethod的职责主要是解析它对应的method的各种参数,比如注解,而解析过程是在ServiceMethod的创建过程中产生的,这就导致ServiceMethod的创建既耗时又耗费资源,所以为ServiceMethod创建了缓存来提高效率。

ServiceMethod.parseAnnotations(this, method)方法通过RequestFactory解析注解,然后返回RequestFactory对象,接着把解析的requestFactoryretrofitmethod传入到HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)方法中。HttpServiceMethod是抽象类ServiceMethod的子类,并实现了ServiceMethod的抽象方法ServiceMethod.invoke方法。

HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)方法中会获取okhttp3.Call.Factory.callFactorycallAdapterresponseConverterRetrofit的配置,为后面创建的retrofit2.OkHttpCall做参数配置。其中callFactoryOkHttpClient对象,是在Retrofit.build方法中创建的。

HttpServiceMethod.invoke方法中会创建retrofit2.OkHttpCall实例,而在retrofit2.OkHttpCall中实际上是使用okhttp3.Call进行网络请求,requestFactory也会用来构造okhttp3.Call对象。在网络请求成功后找到对应的转换器来解析数据。

annotation [ˌænəˈteɪʃ(ə)n] 注释,评注;注释,加注

以下是相关源码:

Retrofit使用建造者模式来创建实例,之后在Retrofit.create方法中使用动态代理技术创建接口(ApiService)的实现类,将接口(ApiServie)中的方法调用集中到InvocationHandler.invoke方法中:

public final class Retrofit {
  public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)
      Proxy.newProxyInstance(
      service.getClassLoader(),
      new Class<?>[] {service},
      new InvocationHandler() {
        private final Platform platform = Platform.get();
        private final Object[] emptyArgs = new Object[0];

        @Override
        public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
          throws Throwable {
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
          args = args != null ? args : emptyArgs;
          return platform.isDefaultMethod(method)
            ? platform.invokeDefaultMethod(method, service, proxy, args)
            : loadServiceMethod(method).invoke(args);
        }
      });
  }
}

InvocationHandler.invoke方法中调用了Retrofit.loadServiceMethod(method)方法来获取ServiceMethodServiceMethod的职责主要是解析它对应的method的各种参数,比如注解,而解析过程是在ServiceMethod的创建过程中产生的,这就导致ServiceMethod的创建既耗时又耗费资源,所以为ServiceMethod创建了缓存来提高效率:

public final class Retrofit {
  ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
}

ServiceMethod.parseAnnotations(this, method)方法通过RequestFactory解析注解,然后返回RequestFactory对象,接着把解析的requestFactoryretrofitmethod传入到HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)方法中。HttpServiceMethod是抽象类ServiceMethod的子类,并实现了ServiceMethod的抽象方法ServiceMethod.invoke方法:

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(
          method,
          "Method return type must not include a type variable or wildcard: %s",
          returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> { 
  ...
}

HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)方法中会获取okhttp3.Call.Factory callFactorycallAdapterresponseConverterRetrofit的配置,为后面创建的retrofit2.OkHttpCall做参数。其中callFactoryOkHttpClient对象,是在Retrofit.build方法中创建的:

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
      Type[] parameterTypes = method.getGenericParameterTypes();
      Type responseType =
        Utils.getParameterLowerBound(
        0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
     
      }

      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }

    CallAdapter<ResponseT, ReturnT> callAdapter =
      createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
      throw methodError(
        method,
        "'"
        + getRawType(responseType).getName()
        + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }

    Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForResponse<>(
        requestFactory,
        callFactory,
        responseConverter,
        (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForBody<>(
        requestFactory,
        callFactory,
        responseConverter,
        (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
        continuationBodyNullable);
    }
  }

  private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
    Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
    try {
      //noinspection unchecked
      return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
  }

  private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
    Retrofit retrofit, Method method, Type responseType) {
    Annotation[] annotations = method.getAnnotations();
    try {
      return retrofit.responseBodyConverter(responseType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create converter for %s", responseType);
    }
  }
  
  final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

  protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
  
  static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

    CallAdapted(
        RequestFactory requestFactory,
        okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

    @Override
    protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
  }
}

public final class Retrofit {
  public static final class Builder {
    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
        new ArrayList<>(
        1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      return new Retrofit(
        callFactory,
        baseUrl,
        unmodifiableList(converterFactories),
        unmodifiableList(callAdapterFactories),
        callbackExecutor,
        validateEagerly);
    }

  }
}

HttpServiceMethod.invoke方法中会创建retrofit2.OkHttpCall实例,而在retrofit2.OkHttpCall中实际上是使用okhttp3.Call进行网络请求,requestFactory也会用来构造okhttp3.Call对象。在网络请求成功后找到对应的转换器来解析数据:

final class OkHttpCall<T> implements Call<T> {
  private final RequestFactory requestFactory;
  private final Object[] args;
  private final okhttp3.Call.Factory callFactory;
  private final Converter<ResponseBody, T> responseConverter;
  
  OkHttpCall(
      RequestFactory requestFactory,
      Object[] args,
      okhttp3.Call.Factory callFactory,
      Converter<ResponseBody, T> responseConverter) {
    this.requestFactory = requestFactory;
    this.args = args;
    this.callFactory = callFactory;
    this.responseConverter = responseConverter;
  }
  
  @Override
  public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = getRawCall();
    }

    if (canceled) {
      call.cancel();
    }

    return parseResponse(call.execute());
  }

  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse =
        rawResponse
            .newBuilder()
            .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();

    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }

    if (code == 204 || code == 205) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }
}

参考

https://zhuanlan.zhihu.com/p/421401880

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值