Retrofit网络请求和响应处理重点分析讲解

 更新时间:2023年03月10日 11:17:07   作者:萌动小彩笔  
这篇文章主要介绍了Retrofit网络请求和响应处理重点分析,在使用 Retrofit发起网络请求时,我们可以通过定义一个接口并使用Retrofit的注解来描述这个接口中的请求,Retrofit会自动生成一个实现该接口的代理对象

在上一篇文章中,我们详细分析了 Retrofit 中的注解解析和动态代理实现,本篇文章将继续深入研究 Retrofit 的核心源码,重点分析 Retrofit 如何进行网络请求和响应处理。

网络请求

在使用 Retrofit 发起网络请求时,我们可以通过定义一个接口并使用 Retrofit 的注解来描述这个接口中的请求,Retrofit 会自动生成一个实现该接口的代理对象。当我们调用这个代理对象的方法时,Retrofit 会根据注解的描述构建一个 Request 对象,并使用 OkHttp 将这个 Request 发送出去。

在 Retrofit 中,我们可以通过 Retrofit#executeRetrofit#enqueue 方法来发送请求。这两个方法的区别在于,execute 方法会阻塞当前线程直到请求完成,而 enqueue 方法会将请求加入到 OkHttp 的请求队列中,并在请求完成时通过回调通知我们。

我们先来看一下 execute 方法的实现:

public <T> T execute(Call<T> call) throws IOException {
  Utils.validateServiceInterface(call.request().tag(), call.request().url().toString());
  return (T) callAdapter(call, call.request().tag()).adapt(call).execute();
}

在这个方法中,首先会对接口进行校验,确保这个接口是有效的。然后我们会根据请求的 Tag 和 URL 来获取适配器callAdapter,并使用适配器来执行请求。

适配器的作用是将请求的参数适配成 OkHttp 能够识别的形式,并将 OkHttp 的响应适配成我们需要的形式。Retrofit 提供了一系列的适配器,包括 Call 适配器、RxJava 适配器、CompletableFuture 适配器等。

我们来看一下 callAdapter 方法的实现:

private CallAdapter<?, ?> callAdapter(Call<?> call, Object tag) {
  Type responseType = call.request().method().equals("HEAD")
      ? Void.class
      : getParameterUpperBound(0, (ParameterizedType) call.request().tag());
  return callAdapter(tag, responseType);
}

在这个方法中,我们首先根据请求的方法来判断响应的类型,如果是 HEAD 方法,那么响应的类型就是 Void;否则我们会通过反射来获取请求的响应类型,并使用这个响应类型来获取适配器。

获取适配器的方法是 callAdapter

public <R, T> CallAdapter<R, T> callAdapter(Object tag, Type returnType) {
  // ...
  for (CallAdapter.Factory factory : adapterFactories) {
    CallAdapter<?, ?> adapter = factory.get(returnType, annotations, this);
    if (adapter != null) {
      return (CallAdapter<R, T>) adapter;
    }
  }
  // ...
}

在这个方法中,我们会遍历所有的适配器工厂,尝试获取适配器。在获取适配器时,我们会将请求的响应类型、注解和 Retrofit 实例作为参数传入。每个适配器工厂都会判断这些参数是否符合自己的适配条件,如果符合,就返回一个适配器实例,否则返回 null。在遍历完所有的适配器工厂之后,如果还没有获取到适配器,那么就会抛出一个异常。

获取到适配器之后,我们就可以使用适配器来执行请求了。在适配器中,我们会将请求参数转换成 OkHttp 的 Request 对象,并将 OkHttp 的 Response 对象转换成我们需要的响应类型。具体的实现可以参考 Retrofit 提供的 CallAdapter 接口。

对于 enqueue 方法,我们可以先来看一下 enqueue 方法的实现:

public <T> void enqueue(Call<T> call, Callback<T> callback) {
  Utils.validateServiceInterface(call.request().tag(), call.request().url().toString());
  callAdapter(call, call.request().tag()).adapt(call).enqueue(new CallbackRunnable<>(callback));
}

在这个方法中,我们首先进行接口校验,然后根据请求的 Tag 和 URL 来获取适配器,并使用适配器来执行请求。不同的是,在 enqueue 方法中,我们将一个 Callback 对象作为参数传入适配器的 enqueue 方法中,以便在请求完成后回调通知我们。

在适配器中,我们可以看到 enqueue 方法的实现:

public void enqueue(final Callback<T> callback) {
  delegate.enqueue(new Callback<Response<T>>() {
    @Override public void onResponse(Call<Response<T>> call, Response<Response<T>> response) {
      Response<T> body;
      try {
        body = response.body();
      } catch (Throwable t) {
        if (response.code() == 204) {
          body = null;
        } else {
          callback.onFailure(call, t);
          return;
        }
      }
      if (response.isSuccessful()) {
        callback.onResponse(call, Response.success(body, response.raw()));
      } else {
        callback.onFailure(call, Response.error(response.errorBody(), response.raw()));
      }
    }
    @Override public void onFailure(Call<Response<T>> call, Throwable t) {
      callback.onFailure(call, t);
    }
  });
}

在这个方法中,我们会将传入的 Callback 对象转换成一个 Callback<Response<T>> 对象,并使用这个对象来调用 OkHttp 的 enqueue 方法。在请求完成后,我们会将 OkHttp 的 Response 对象转换成 Retrofit 的 Response 对象,并根据响应码来判断请求的结果。如果响应码表示请求成功,那么我们就调用 Callback 对象的 onResponse 方法;否则就调用 Callback 对象的 onFailure 方法。

响应处理

在 Retrofit 中,我们可以通过定义一个接口并使用注解来描述我们期望的请求格式和响应格式。例如,我们可以通过 @GET 注解来描述一个 GET 请求,使用 @Query 注解来描述请求参数,使用 @Body 注解来描述请求体,使用 @Headers 注解来描述请求头等。

在执行请求时,Retrofit 会根据这些注解来自动生成一个对应的请求对象,并将请求对象转换成 OkHttp 的 Request 对象。在接收响应时,Retrofit 会将 OkHttp 的 Response 对象转换成一个对应的响应对象,并将响应对象中的数据转换成我们需要的数据类型。这些转换工作是通过 Retrofit 的转换器来完成的,Retrofit 中默认提供了两个转换器:GsonConverterFactoryJacksonConverterFactory。我们也可以自定义一个转换器来实现我们期望的数据转换。

在 Retrofit 类的构造方法中,我们可以看到 Retrofit 默认使用了 Platform.get() 方法来获取当前运行平台的默认转换器工厂,并将其添加到 converterFactories 中。然后,我们可以使用 addConverterFactory 方法来添加自定义的转换器工厂。

public Retrofit(Builder builder) {
  // ...
  if (builder.converterFactories == null) {
    converterFactories.add(Platform.get().defaultConverterFactory());
  } else {
    converterFactories.addAll(builder.converterFactories);
  }
  // ...
}
public interface Platform {
  // ...
  Converter.Factory defaultConverterFactory();
}

execute方法中,我们会调用适配器的 adapt 方法来执行请求,并将返回的 Call 对象转换成一个响应对象。在转换过程中,我们会根据响应类型来选择对应的转换器来进行转换。具体的转换实现可以参考 Retrofit 提供的 Converter 接口和 Converter.Factory 接口。

public <T> T execute(Call<T> call) throws IOException {
  // ...
  Response<T> response = call.execute();
  if (response.isSuccessful()) {
    return response.body();
  } else {
    Converter<ResponseBody, ErrorResponse> converter = retrofit.responseBodyConverter(
        ErrorResponse.class, new Annotation[0]);
    throw new ApiException(converter.convert(response.errorBody()));
  }
}
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public <T> T adapt(Call<T> call) {
  return (T) new OkHttpCall<>(requestFactory, callFactory, converter, call);
}
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
  return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
    @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
  Objects.requireNonNull(type, "type == null");
  Objects.requireNonNull(annotations, "annotations == null");
  int start = converterFactories.indexOf(skipPast) + 1;
  for (int i = start, count = converterFactories.size(); i < count; i++) {
    Converter<ResponseBody, ?> converter =
        converterFactories.get(i).responseBodyConverter(type, annotations, this);
    if (converter != null) {
      return (Converter<ResponseBody, T>) converter;
    }
  }
  throw new IllegalArgumentException(
      "Could not locate ResponseBody converter for " + type + " with annotations " + Arrays.toString(annotations));
}

以上是 Retrofit 中处理响应的核心代码。当我们执行一个请求时,Retrofit 会先将请求转换成 OkHttp 的 Request 对象并发送出去,然后等待响应返回。当响应返回时,Retrofit 会将响应转换成一个响应对象,并将响应对象中的数据转换成我们期望的数据类型。这个过程中,我们可以使用 Retrofit 提供的转换器来自定义数据的转换规则。

下面是一个示例,演示了如何使用 Retrofit 来发送一个 GET 请求并将响应中的 JSON 数据转换成一个 Java 对象:

public interface ApiService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<Repo>> call = apiService.listRepos("smallmarker");
List<Repo> repos = call.execute().body();

在上面的示例中,我们首先使用 Retrofit 构建器创建一个 Retrofit 实例,并指定了请求的基础 URL 和转换器工厂。然后,我们通过调用 create 方法来创建一个 ApiService 的代理对象。最后,我们调用 listRepos 方法来发送一个 GET 请求。

在上面的示例中,我们使用了 Retrofit 的 GsonConverterFactory 来将响应体中的 JSON 数据转换成 Java 对象。具体实现可以查看 Retrofit 提供的 GsonConverterFactory 类。

public final class GsonConverterFactory extends Converter.Factory {
  private final Gson gson;
  private GsonConverterFactory(Gson gson) {
    this.gson = gson;
  }
  public static GsonConverterFactory create() {
    return create(new Gson());
  }
  public static GsonConverterFactory create(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    return new GsonConverterFactory(gson);
  }
  @Override
  public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }
  @Override
  public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
                                                                  Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
}

可以看到,GsonConverterFactory 继承了 Retrofit 的 Converter.Factory 类,并重写了其中的 responseBodyConverter 方法和 requestBodyConverter 方法。在 responseBodyConverter 方法中,我们将响应体中的 JSON 数据转换成 Java 对象,而在 requestBodyConverter 方法中,我们将 Java 对象转换成请求体中的 JSON 数据。

除了 GsonConverterFactory 以外,Retrofit 还提供了其他的转换器,如 JacksonConverterFactory、MoshiConverterFactory 等,我们可以根据需要选择适合自己的转换器。

总的来说,Retrofit 中网络请求和响应处理的核心代码非常简洁明了。我们只需要通过定义接口来描述请求和响应,然后使用 Retrofit 的动态代理机制来将接口转换成一个实际的实现类,并通过 Retrofit 的配置来指定请求和响应的转换器即可。这种方式大大简化了网络请求的流程,使得我们可以更加专注于业务逻辑的处理。

到此这篇关于Retrofit网络请求和响应处理重点分析讲解的文章就介绍到这了,更多相关Retrofit网络请求和响应处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • android studio集成ijkplayer的示例代码

    android studio集成ijkplayer的示例代码

    本篇文章主要介绍了android studio集成ijkplayer的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • android全屏去掉title栏的多种实现方法

    android全屏去掉title栏的多种实现方法

    android全屏去掉title栏包括以下几个部分:实现应用中的所有activity都全屏/实现单个activity全屏/实现单个activity去掉title栏/自定义标题内容/自定义标题布局等等感兴趣的可参考下啊
    2013-02-02
  • android实现可上下回弹的scrollview

    android实现可上下回弹的scrollview

    这篇文章主要为大家详细介绍了android实现可上下回弹的scrollview,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Android数据加密之Base64编码算法的简单实现

    Android数据加密之Base64编码算法的简单实现

    下面小编就为大家带来一篇Android数据加密之Base64编码算法的简单实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10
  • android Palette调色板使用详解

    android Palette调色板使用详解

    本篇文章主要介绍了android Palette调色板使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Kotlin整合Vertx开发Web应用

    Kotlin整合Vertx开发Web应用

    这篇文章主要介绍了Kotlin整合Vertx开发Web应用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02
  • 详解VirtualApk启动插件Activity

    详解VirtualApk启动插件Activity

    这篇文章主要介绍了详解VirtualApk启动插件Activity,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • Android自定义控件之广告条滚动效果

    Android自定义控件之广告条滚动效果

    这篇文章主要为大家详细介绍了Android自定义控件之广告条滚动效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Android Git submodule详解用法示例

    Android Git submodule详解用法示例

    项目中经常会使用到第三方的 git 库, 将三方库整合到项目中最简单的办法就是复制粘贴, 但是如果这个库升级了一个很酷炫的功能, 你要怎么整合进来呢?(其实就是 git 版的包管理器)这就是本次要介绍的 git-submodule 操作, 直接把第三方的版本库合并到自己的库中
    2021-11-11
  • Android实现保存QQ账号与密码功能(文件存储)

    Android实现保存QQ账号与密码功能(文件存储)

    这篇文章主要介绍了Android保存QQ账号与密码,文件存储是Android中最基本的一种数据存储方式,它与Java中的文件存储类似,都是通过I/O流形式把数据直接存储到文件中,下面我们一起来看一下如何用Android实现文件存储功能吧
    2022-04-04

最新评论