Retrofit网络请求和响应处理源码分析
本篇内容主要讲解“Retrofit网络请求和响应处理源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Retrofit网络请求和响应处理源码分析”吧!
网络请求
在使用 Retrofit 发起网络请求时,我们可以通过定义一个接口并使用 Retrofit 的注解来描述这个接口中的请求,Retrofit 会自动生成一个实现该接口的代理对象。当我们调用这个代理对象的方法时,Retrofit 会根据注解的描述构建一个 Request 对象,并使用 OkHttp 将这个 Request 发送出去。
在 Retrofit 中,我们可以通过 Retrofit#execute
或 Retrofit#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 中默认提供了两个转换器:GsonConverterFactory
和 JacksonConverterFactory
。我们也可以自定义一个转换器来实现我们期望的数据转换。
在 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网络请求和响应处理源码分析”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341