Connor学Android - Retrofit基本使用与源码分析

2023-10-19 00:20

本文主要是介绍Connor学Android - Retrofit基本使用与源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

Learn && Live

虚度年华浮萍于世,勤学善思至死不渝

前言

Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/KDbOT,话不多说我们马上开始!

Retrofit 是 OkHttp 的加强版,底层封装了 OkHttp。准确来说,Retrofit 是一个 RESTful 的 http 网络请求框架的封装。因为网络请求工作本质上是由OkHttp 来完成,而 Retrofit 负责网络请求接口的封装。

1.注解

Retrofit 与其他请求框架不同的是,它使用了注解。注解分为三大类,分别是 HTTP 请求方法注解、标记类注解和参数类注解

HTTP 请求方法注解

(1)共8种,分别是 GET、POST、PUT、DELETE、HEAD、PATCH、OPTIONS、HTTP

(2)前七种分别对应 HTTP 的请求方法,HTTP 则可以替换以上7中,也可以扩展请求方法

标记类注解

共3种,分别是 FormUrlEncoded、Multipart、Streaming

(1)FormUrilEncoded:表示请求发送编码表单数据,每个键值对需要使用 @Filed 注解

(2)Multipart:在 @Part 之前使用,标明可上传1多个文件

(3)Streaming 代表响应的数据以流的形式返回,不使用则默认把全部数据加载到内存,所以下载大文件时需要加上这个注解

参数类注解

有多种,常用的有 Header、Headers、Body、Path、Field、FieldMap、Part、PartMap、Query 和 QueryMap 等

(1)Header:添加消息报头,有两种方式:静态添加和动态添加

  • 静态添加:使用 @Header 在方法上方指定,如果添加多个需要使用 {} 包含起来
  • 动态添加:使用 @Header 在方法形参处指定,通过调用该方法来动态地添加消息报头

(2)Body:将 JSON 字符串作为请求体发送到服务器,用 @Body 标注参数对象即可,Retrofit 会将对象转换为字符串

(3)Path:用于动态地配置 URL 地址

(4)Field:将键值对类型数据发送到服务器

  • 首先使用 @FormUrlEncoded 注解来标明这是一个表单请求
  • 然后在方法中使用 @Field 注解来标注对应的 String 类型的 key,从而组成一组键值对进行传递

(5)Part:用于单个文件上传,常标注 MultipartBody.Part 类型的形参,用于指定准备上传的图片文件

(6)PartMap:用于多个文件的上传,使用 Map 封装所有准备上传的文件

(4)Query:用于动态地配置 IP 地址,注意与 URL 的区别

(5)QueryMap:在网络请求中一般需要传入多个查询参数,可以采用 QueryMap,将所有参数集成在一个 Map 种一起传递

2.使用

GET 请求访问网络

(1)定义请求网络接口,其内定义被 @GET 等注解标注、返回值为 Call 的方法

(2)通过 Builder() 创建 Retrofit 对象,设置 url 等参数

(3)通过 Retrofit 动态代理获取之前定义的接口,并通过接口定义的方法得到 Call 对象

(4)调用 enqueue() 方法完成异步请求并处理回调,回调的 Callback 是运行在 UI 线程的

(5)如果想同步请求网络,使用 execute(),取消则使用 cancel()

public interface IpService {@GET{"getIpInfo.php?ip=59.108.54.37"}Call<IpModel> getIpMsg();
}
String url = "...";
Retrofit retrofit = new Retrofit.Builder().baseUrl(url).addConverterFactory(GsonConverterFactory.create()).build();
IpService ipService = retrofit.create(IpService.class);
Call<IpModel> call = ipService.getIpMsg() {@Overridepublic void onResponse(Call<IpModel> call, Response<IpModel> response) {String country = response.body().getData().getCountry();...}@Overridepublic void onFailure(Call<IpModel> call, Throwable t) {}
}

POST 请求访问网络

大致步骤与 GET 相同,只是根据传输实体的类型改变网络访问接口种方法的注解即可

3.创建 Retrofit 对象的源码分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pr3a8PRW-1663033884026)(F:\yhy925\dailylife\Work\2.Android\2.《Android进阶之光》\《Android进阶之光》知识点总结\4.网络框架\1.Retrofit源码分析1.png)]

步骤1

Retrofit 内部包含了一些重要的属性,需要在创建 Retrofit 时配置这些属性

(1)serviceMethodCache

  • 网络请求配置对象,即对网络请求接口中方法注解进行解析后得到的对象
  • 作用:存储网络请求相关的配置,如网络请求的方法、数据转换器、网络请求适配器、网络请求工厂、基地址等

(2)baseUrl:网络请求的 url 地址,必须指定,否则抛出 IllegalStateException

(3)callFactory:网络请求器的工厂,用于生成网络请求器 Call,默认使用 OkHttp

(4)adapterFactories:网络请求适配器工厂的集合,网络请求适配器工厂用于生产网络适配器 CallAdapter

(5)converterFactories:数据转换器工厂的集合,数据转换器工厂用于生产数据转换器 Converter

(6)callbackExecutor:回调方法执行器

public final class Retrofit {private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();private final HttpUrl baseUrl;private final okhttp3.Call.Factory callFactory;private final List<CallAdapter.Factory> adapterFactories;private final List<Converter.Factory> converterFactories;private final Executor callbackExecutor;private final boolean validateEagerly; // Retrofit类的构造函数Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,Executor callbackExecutor, boolean validateEagerly) {this.callFactory = callFactory;this.baseUrl = baseUrl;this.converterFactories = unmodifiableList(converterFactories);this.adapterFactories = unmodifiableList(adapterFactories);// unmodifiableList(list)近似于UnmodifiableList<E>(list)// 作用:创建的新对象能够对list数据进行访问,但不可通过该对象对list集合中的元素进行修改this.callbackExecutor = callbackExecutor;this.validateEagerly = validateEagerly;...}
}

CallAdapter

(1)定义了网络请求器 Call 的适配器

(2)Call 在 Retrofit 里默认是 OkHttpCall

(3)这个适配器用于将默认的网络请求器 OkHttpCall 转换成适合被不同平台来调用的网络请求器

(4)Retrofit 提供四种 CallAdapterFactory:Executor(默认)、Guava、Java8、RxJavaCallAdapterFactory

(5)比如在配合 RxJava 使用时可以通过 RxJavaCallAdapterFactory 中的 CallAdapter 将 OkHttpCall 转换为 RxJava(Scheduler)

步骤2

这一阶段会通过 Builder 完成 Retrofit 的创建及内部平台类型、网络请求适配器工厂、数据转换器工厂、回调执行器的初始化

(1)首先因为 Retrofit 的创建是通过建造者模式实现的,所以 Builder 的属性大体与 Retrofit 一致

(2)在 Builder 的无参构造器种会调用 Platfrom 的 get() 方法,get() 方法内会进一步调用 findPlatform() 方法

(3)findPlatfrom() 方法会根据不同的运行平台返回不同的 Platform 给 Builder 的有参构造器,支持的有 Android、Java8、IOS

(4)假设拿到的 Platform 是 Android,在创建并返回这个 Android 对象时会主要做如下操作

  • 创建默认的 CallAdapterFactory,即 Executor…,由它生产的 Adapter 可以使 Call 在异步调用时在指定的Executor 上执行回调
  • 返回默认的 CallbackExecutor,用于完成子线程到主线程的切换,并在主线程中执行回调方法
  • 获取与主线程绑定的 Handler,用于在主线程中对网络请求返回数据的处理
  • 切换线程的流程
    • 回调 ExecutorCallAdapterFactory 生成了一个 ExecutorCallbackCall 对象
    • 调用 ExecutorCallbackCall.enqueue(CallBack) 从而调用 MainThreadExecutor 的 execute() 通过 handler 切换到主线程

(5)再回来看 Builder 的有参构造器,内部会完成如下操作

  • 首先接收传过来的 Platform
  • 通过创建 BuiltInConverters 对象创建 Conveter 并加入 ConveterFactory
    • converterFactories 是一个存放数据转换器 Converter.Factory 的数组,配置 converterFactories 即配置里面的数据转换器
    • BuiltInConverters 是内置的数据转换器工厂(继承Converter.Factory类),可通过 new BuiltInConverters() 初始化数据转换器
<-- Builder-->
public static final class Builder {private Platform platform;private okhttp3.Call.Factory callFactory;private HttpUrl baseUrl;private List<Converter.Factory> converterFactories = new ArrayList<>();private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();private Executor callbackExecutor;private boolean validateEagerly;public Builder() {this(Platform.get());}...
}class Platform {private static final Platform PLATFORM = findPlatform();static Platform get() { return PLATFORM; }private static Platform findPlatform() {try {Class.forName("android.os.Build");if (Build.VERSION.SDK_INT != 0) {return new Android(); }} catch (ClassNotFoundException ignored) {}try {Class.forName("java.util.Optional");return new Java8();} catch (ClassNotFoundException ignored) {}try {Class.forName("org.robovm.apple.foundation.NSObject");return new IOS();} catch (ClassNotFoundException ignored) {}return new Platform();}...
}static class Android extends Platform {@OverrideCallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {return new ExecutorCallAdapterFactory(callbackExecutor);}@Override public Executor defaultCallbackExecutor() {return new MainThreadExecutor();}static class MainThreadExecutor implements Executor {private final Handler handler = new Handler(Looper.getMainLooper());@Overridepublic void execute(Runnable r) {handler.post(r);}}
}public  Builder(Platform platform) {this.platform = platform;    converterFactories.add(new BuiltInConverters());
}
步骤3

baseUrl() 用于配置并处理 Retrofit 类的网络请求 Url 地址

(1)baseUrl() 方法会将传入的 String 参数转换为适合 OkHttp 的 HttpUrl 类型,并传给带 HttpUrl 类型参数的 baseUrl() 方法

(2)这个方法中会对 URL 做一定的处理

  • 把 URL 参数分割成几个路径碎片
  • 通过检测最后一个碎片来检查 URL 参数是不是以"/"结尾,不是则抛出 IllegalArgumentException
public Builder baseUrl(String baseUrl) {HttpUrl httpUrl = HttpUrl.parse(baseUrl);return baseUrl(httpUrl);
}public Builder baseUrl(HttpUrl baseUrl) {List<String> pathSegments = baseUrl.pathSegments();	if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);}	this.baseUrl = baseUrl;return this;
}
步骤4

(1)创建一个含有Gson对象实例的 GsonConverterFactory 并放入到数据转换器工厂converterFactories 里

(2)即 Retrofit 默认使用 Gson 进行解析

(3)若使用其他解析方式(如Json、XML或Protocobuf),也可通过自定义数据解析器来实现(必须继承 Converter.Factory)

public final class GsonConverterFactory extends Converter.Factory {public static GsonConverterFactory create() {return create(new Gson());}public static GsonConverterFactory create(Gson gson) {return new GsonConverterFactory(gson); ->>步骤3}private final Gson gson;private GsonConverterFactory(Gson gson) {if (gson == null) throw new NullPointerException("gson == null");this.gson = gson;}
}public Builder addConverterFactory(Converter.Factory factory) {converterFactories.add(checkNotNull(factory, "factory == null"));return this;
}
步骤5

调用 build() 方法,通过前面步骤设置的变量,将 Retrofit 类的所有成员变量都配置完毕。build() 方法主要完成

(1)首先保证 baseUrl 不为空

(2)配置 CallFactory,如果没指定,则默认使用okhttp,及创建一个 OkHttpClient 对象

(3)配置 CallbackExecutor,如果没指定,则默认使用之前 Platform 检测环境时的默认 callbackExecutor

(4)配置 CallAdapterFactory,添加了步骤2中创建的 CallAdapter.Factory 请求适配器(添加在集合器末尾,自定义的在前)

(5)配置 ConverterFactory

  • 在步骤2中已经添加了内置的数据转换器 BuiltInConverters()(添加到集合器的首位)
  • 在步骤4中又插入了一个 Gson 的转换器 - GsonConverterFactory(添加到集合器的首二位)
  • 自定义的转换器加在后面

(6)上边提到了集合中的顺序问题,获取时都是从集合的首位到末位开始遍历,因此集合中的工厂位置越靠前就拥有越高的使用权限

(7)最后返回这个配置好的 Retrofit 对象

public Retrofit build() {okhttp3.Call.Factory callFactory = this.callFactory;if (callFactory == null) {callFactory = new OkHttpClient();}Executor callbackExecutor = this.callbackExecutor;if (callbackExecutor == null) {callbackExecutor = platform.defaultCallbackExecutor();}List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly);
}

4.创建网络请求接口实例的源码分析

AccessApi NetService = retrofit.create(NetService.class);

create() 方法主要完成两个操作

(1)首先判断是否需要提前验证,需要则调用 eagerlyValidateMethods(service) 方法完成提前验证,这个方法的主要作用是

  • 给接口中每个方法(Method)的注解进行解析并得到一个 ServiceMethod 对象
  • 以 Method 为键、以 ServiceMethod 为值将该对象存入 LinkedHashMap 集合中
  • 注意这里的解析和存储操作由 loadServiceMethod() 方法完成,这里后续会介绍到

(2)否则会完成动态解析过程,即创建一个网络请求接口的动态代理对象,该动态代理是为了拿到网络请求接口实例上的所有注解

(3)create() 方法最终会返回一个 Proxy.newProxyInstance 动态代理对象,这个方法需要三个参数

  • service.getClassLoader(),指定用被代理接口的类加载器,用于动态生成接口的实现类
  • new Class<?>[] { service },动态创建实现类的实例
  • InvocationHandler:调用网络请求接口的方法实际上是通过调用 InvocationHandler 对象的 invoke() 方法来完成指定的功能
public <T> T create(final Class<T> service) {if (validateEagerly) {eagerlyValidateMethods(service);}return (T) Proxy.newProxyInstance(service.getClassLoader(),new Class<?>[] { service },new InvocationHandler() {private final Platform platform = Platform.get();@Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable {ServiceMethod serviceMethod = loadServiceMethod(method);OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.callAdapter.adapt(okHttpCall);}});
}private void eagerlyValidateMethods(Class<?> service) {  Platform platform = Platform.get();for (Method method : service.getDeclaredMethods()) {if (!platform.isDefaultMethod(method)) {  loadServiceMethod(method); } }
}

下面来分析这个 invoke() 方法,这个方法总共有三个语句,将分别进行分析

1.ServiceMethod serviceMethod = loadServiceMethod(method);

(1)loadServiceMethod() 主要作用是读取网络请求接口里的方法,并根据前面配置好的属性配置 serviceMethod 对象

(2)会从 serviceMethodCache 中查询传入的方法对应的 ServiceMethod 是否有缓存

  • 若有,则直接通过 get() 方法取出缓存中的 ServiceMethod
  • 若没有,则通过 ServiceMethod 的 Builder 创建 ServiceMethod 并通过 put() 方法存储缓存
ServiceMethod loadServiceMethod(Method method) {ServiceMethod result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {result = new ServiceMethod.Builder(this, method).build();serviceMethodCache.put(method, result);}}return result;
}

(3)ServiceMethod 的创建过程可分为3个步骤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RREGzpyZ-1663033884027)(F:\yhy925\dailylife\Work\2.Android\2.《Android进阶之光》\《Android进阶之光》知识点总结\4.网络框架\2.ServiceMethod.png)]

步骤1 ServiceMethod 类构造器

ServiceMethod 对象包含了访问网络需要的所有基本信息,其构造器的作用就是通过 Builder 配置好这些基本信息

(1)callFactory:网络请求工厂

(2)callAdapter:网络请求适配器工厂

(3)responseConverter:Response 转换器,负责把服务器返回的数据(JSON或键值对,由 ResponseBody 封装)转化为 T 类型的对象

(4)baseUrl:网络请求地址

(5)relativeUrl:网络请求的相对地址

(6)httpMethod:网络请求的 Http 方法

(7)headers:网络请求头

(8)contentType:网络请求的 Http 报文 body 的类型

(9)parameterHandlers:方法参数处理器,负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数;

public final class ServiceMethod {final okhttp3.Call.Factory callFactory;   // 网络请求工厂  final CallAdapter<?> callAdapter;  private final Converter<ResponseBody, T> responseConverter; private final HttpUrl baseUrl;private final String relativeUrl;private final String httpMethod;private final Headers headers;private final MediaType contentType; private final ParameterHandler<?>[] parameterHandlers;  ServiceMethod(Builder<T> builder) {this.callFactory = builder.retrofit.callFactory();  this.callAdapter = builder.callAdapter;   this.responseConverter = builder.responseConverter;   this.baseUrl = builder.retrofit.baseUrl();   this.relativeUrl = builder.relativeUrl;   this.httpMethod = builder.httpMethod;  this.headers = builder.headers;  this.contentType = builder.contentType; .  this.hasBody = builder.hasBody; y  this.isFormEncoded = builder.isFormEncoded;   this.isMultipart = builder.isMultipart;  this.parameterHandlers = builder.parameterHandlers;  }....
}

步骤2 ServiceMethod 的 Builder()

(1)将当前的 ServiceMethod 对象与所属的 Retrofit 对象和对应的 Method 对象进行绑定

(2)通过 Method.getAnnotations() 获取网络请求接口方法里的注解

(3)通过 Method.getGenericParameterTypes() 获取网络请求接口方法里的参数类型

(4)通过 Method.getParameterAnnotations() 获取网络请求接口方法中参数的注解

public Builder(Retrofit retrofit, Method method) {this.retrofit = retrofit;this.method = method;this.methodAnnotations = method.getAnnotations();this.parameterTypes = method.getGenericParameterTypes();	this.parameterAnnotationsArray = method.getParameterAnnotations();	
}

步骤3 ServiceMethod 的 build()

build() 方法内会完成所有基本信息的配置

(1)callAdapter:调用 createCallAdapter() 方法,根据接口方法的返回值和注解类型,从 Retrofit 中获取对应的网络请求适配器

  • createCallAdapter() 方法中,首先会通过 Method.getGenericReturnType() 方法获取网络请求接口里方法的返回值类型

  • 再通过 method.getAnnotations() 方法获取网络请求接口方法前的注解,如 @GET 或 @POST

  • 最后根据返回值和注解类型,调用 Retrofit 的 callAdapter() 方法,从 Retrofit 对象中获取对应的网络请求适配器

  • callAdapter() 方法内调用 nextCallAdapter() 方法,该方法会遍历 CallAdapter.Factory 集合,根据返回值和注解寻找合适的工厂生产

(2)responseType:调用 callAdapter 的 responseType() 方法,根据方法返回值和注解类型,获取该网络适配器返回的数据类型

(3)responseConverter:调用 createResponseConverter() 方法,根据方法返回值和注解类型,从 Retrofit 中获取对应的数据转换器

  • createResponseConverter() 方法内会调用 Retrofit 的 responseBodyConverter() 方法
  • responseBodyConverter() 方法继续调用 nextResponseBodyConverter() 方法
  • nextResponseBodyConverter() 方法会遍历 Converter.Factory 集合,根据返回值和注解寻找合适的工厂生产
    • @Body 和 @Part 类型的参数利用 Converter.Factory 提供的 requestBodyConverter 进行转换
    • 其余类型的参数都利用 Converter.Factory 的stringConverter 进行转换
  • 由于构造Retroifit采用的是Gson解析方式,所以取出的是GsonResponseBodyConverter

(4)parseMethodAnnotation():解析网络请求接口中方法的注解,主要是解析获取 Http 请求的方法

(5)parseHttpMethodAndPath():根据注解对ServiceMethod中的httpMethod、relativeUrl、relativeUrlParamNames域进行赋值

(6)parameterCount:获取当前方法的参数数量

(7)为方法中的每个参数创建一个ParameterHandler<?>对象并解析每个参数使用的注解类型,该对象的创建过程就是对方法参数中注解进行解析,这里的注解包括:Body、PartMap、Part、FieldMap、Field、Header、QueryMap、Query、Path、Url

public ServiceMethod build() {callAdapter = createCallAdapter();responseType = callAdapter.responseType();responseConverter = createResponseConverter();	for (Annotation annotation : methodAnnotations) {parseMethodAnnotation(annotation);}int parameterCount = parameterAnnotationsArray.length;parameterHandlers = new ParameterHandler<?>[parameterCount];for (int p = 0; p < parameterCount; p++) {Type parameterType = parameterTypes[p];Annotation[] parameterAnnotations = parameterAnnotationsArray[p];parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);}return new ServiceMethod<>(this);
}private CallAdapter<?> createCallAdapter() {Type returnType = method.getGenericReturnType();		Annotation[] annotations = method.getAnnotations();		try {return retrofit.callAdapter(returnType, annotations);	}...
}public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {for (int i = start, count = adapterFactories.size(); i < count; i++) {CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);		if (adapter != null) {return adapter;}}
}private Converter<ResponseBody, T> createResponseConverter() {Annotation[] annotations = method.getAnnotations();try {return retrofit.responseBodyConverter(responseType, annotations);} catch (RuntimeException e) { throw methodError(e, "Unable to create converter for %s", responseType);}
}public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {return nextResponseBodyConverter(null, type, annotations);
}public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,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);	
}

至此就成功创建出了包含注解信息、网络请求信息的 serviceMethod 对象

2.OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

根据第一步配置好的 ServiceMethod 对象和输入的请求参数创建 OkHttpCall 对象

3.return serviceMethod.callAdapter.adapt(okHttpCall);

(1)将第二步创建的 OkHttpCall 对象传给第一步的 serviceMethod 对象中对应的网络请求适配器工厂的 adapt()

(2)adapt() 方法会对 Call 对象进行静态代理,并最终返回一个 ExecutorCallbackCall

(3)ExecutorCallbackCall 是对 Call 的封装,包含两个重要属性:delegate 和 callbackExecutor

  • delegate 是 OkHttpCall 的静态代理对象,后续通过 Call 调用 enqueue() 方法时实际上是通过这个 delegate 来调用的
  • callbackExecutor 则是回调方法执行器,用于切换线程,将请求回调到主线程
public <R> Call<R> adapt(Call<R> call) {return new ExecutorCallbackCall<>(callbackExecutor, call);  
}ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {this.delegate = delegate;this.callbackExecutor = callbackExecutor;	
}

5.创建 Call 对象的源码分析

Call<JavaBean> call = NetService.getCall();

(1)NetService 对象实际上是动态代理对象 Proxy.newProxyInstance(),并不是真正的网络请求接口创建的对象

(2)当NetService对象调用 getCall() 时会被动态代理对象拦截,然后调用自身的 InvocationHandler.invoke() 方法

(3)接下来利用Java反射获取到 getCall() 的注解信息,配合传入的 args 参数创建ServiceMethod对象

6.异步请求 OkHttpCall.enqueue() 的源码分析

步骤1:对网络请求接口的方法中的每个参数利用对应 ParameterHandler 进行解析,再根据 ServiceMethod 对象创建一个 OkHttp的Request 对象

步骤2:使用 OkHttp 的 Request 发送网络请求;

步骤3:对返回的数据使用之前设置的数据转换器(GsonConverterFactory)解析返回的数据,最终得到一个 Response<T> 对象

步骤4:进行线程切换从而在主线程处理返回的数据结果,若使用了RxJava,则直接回调到主线程

具体的源码分析过程如下:

如前面所说,delegate 是 ExecutorCallbackCall 为 Call 提供的静态代理对象,所以调用 Call 的 enqueue 时,实际调用的是 delegate 的

delegate.enqueue()

(1)创建 OkHttp 的 Request 对象,再封装成 okhttp.Call,这里会调用 createRawCall() 方法来完成

  • 从 ServiceMethod 的 toRequest() 返回一个 Request 对象

  • 根据 Request 对象,借助 ServiceMethod 中的 callFactory 创建 一个okhttp3.Call 对象

(2)发送异步网络请求,因为 delegate 是 Call 的静态代理对象,所以实际上还是调用的 Call 的 enqueue() 方法

(3)解析返回的数据,这里会调用 parseResponse() 解析拿到的 OkHttp3 返回的 Response 对象

  • 收到返回数据后,首先进行状态码检查
  • 通过状态码检查后,将 response body 传入 ServiceMethod 中,ServiceMethod 通过调用 Converter 接口(之前设置的GsonConverterFactory)将 response body 转成一个 Java 对象,即解析返回的数据
  • 最后返回一个包含数据的 Response 对象
@Override 
public void enqueue(final Callback<T> callback) {okhttp3.Call call;Throwable failure;synchronized (this) {if (executed) throw new IllegalStateException("Already executed.");executed = true;call = rawCall;failure = creationFailure;if (call == null && failure == null) {try {call = rawCall = createRawCall();	} catch (Throwable t) {failure = creationFailure = t;}}call.enqueue(new okhttp3.Callback() {@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) throws IOException {Response<T> response;try {response = parseResponse(rawResponse);} catch (Throwable e) {callFailure(e);return;}callSuccess(response);}@Override public void onFailure(okhttp3.Call call, IOException e) {try {callback.onFailure(OkHttpCall.this, e);} catch (Throwable t) {t.printStackTrace();}}private void callFailure(Throwable e) {try {callback.onFailure(OkHttpCall.this, e);} catch (Throwable t) {t.printStackTrace();}}private void callSuccess(Response<T> response) {try {callback.onResponse(OkHttpCall.this, response);} catch (Throwable t) {t.printStackTrace();}}});}
}private okhttp3.Call createRawCall() throws IOException {Request request = serviceMethod.toRequest(args);okhttp3.Call call = serviceMethod.callFactory.newCall(request);if (call == null) {throw new NullPointerException("Call.Factory returned null.");}return call;
}Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {ResponseBody rawBody = rawResponse.body();rawResponse = rawResponse.newBuilder().body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())).build();int code = rawResponse.code();if (code < 200 || code >= 300) {}if (code == 204 || code == 205) {return Response.success(null, rawResponse);}ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);try {T body = serviceMethod.toResponse(catchingBody);return Response.success(body, rawResponse);} catch (RuntimeException e) {...}
}

call.enqueue()

接下来回到 Call 的 enqueue 方法,在拿到请求返回的数据后,在该方法内完成线程的切换

(1)最后 OkHttp 的异步请求结果返回到 callbackExecuto

(2)callbackExecutor.execute() 通过 Handler 异步回调将结果传回到主线程进行处理,即进行了线程切换

(3)线程切换的流程已在前面提到(Android 类),这里不再复述

@Override 
public void enqueue(final Callback<T> callback) {delegate.enqueue(new Callback<T>() {@Override public void onResponse(Call<T> call, final Response<T> response) {callbackExecutor.execute(new Runnable() {@Override public void run() {if (delegate.isCanceled()) {callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));} else {callback.onResponse(ExecutorCallbackCall.this, response);}}});}@Override public void onFailure(Call<T> call, final Throwable t) {callbackExecutor.execute(new Runnable() {@Override public void run() {callback.onFailure(ExecutorCallbackCall.this, t);}});}});
}

7.源码中应用的设计模式

Retrofit采用了外观模式统一调用创建网络请求接口实例和网络请求参数配置的方法,具体细节是:

(1)动态创建网络请求接口的实例(代理模式 - 动态代理

(2)创建 serviceMethod 对象(建造者模式 & 单例模式(缓存机制)

(3)对 serviceMethod 对象进行网络请求参数配置:通过解析网络请求接口方法的参数、返回值和注解类型,从Retrofit对象中获取对应的网络请求的url地址、网络请求执行器、网络请求适配器 & 数据转换器。(策略模式

(4)对 serviceMethod 对象加入线程切换的操作,便于接收数据后通过Handler从子线程切换到主线程从而对返回数据结果进行处理(装饰模式

最终创建并返回一个OkHttpCall类型的网络请求对象

这篇关于Connor学Android - Retrofit基本使用与源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/236068

相关文章

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

golang1.23版本之前 Timer Reset方法无法正确使用

《golang1.23版本之前TimerReset方法无法正确使用》在Go1.23之前,使用`time.Reset`函数时需要先调用`Stop`并明确从timer的channel中抽取出东西,以避... 目录golang1.23 之前 Reset ​到底有什么问题golang1.23 之前到底应该如何正确的

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三