[旧][Android] Retrofit 源码分析之 ServiceMethod 对象
备注
原发表于2016.05.03,资料已过时,仅作备份,谨慎参考
前言
大家好,我又来学习 Retrofit 了,可能这是最后一篇关于 Retrofit 框架的文章了。我发现源码分析这回事,当时看明白了,过些时候再看就想这写的啥玩意。所以大家还是多看多学多分析。
另外跟我自己文章结构组织也有很大关系,我尽量在以后加强这点,做到简洁清晰有层次。

这周我们要分析一下 ServiceMethod。
ServiceMethod 是什么
ServiceMethod 会将你的接口方法调用转化为一个 Call 对象。也就说对于每一个接口方法,他都会创建一个与之对应的 ServiceMethod,实际上上篇文章也有提到。
那么 ServiceMethod 是从哪开始的呢?
MyApi api = retrofit.create(MyApi.class);
这句代码总没有忘记吧?
create 方法根据你的『Http 接口』返回了一个『动态代理』,当我们调用接口方法时,会转发到『动态代理』的 invoke 方法。内有三句重点:
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
最后一句就不说了,把你的 OkHttpCall 转化适配成指定类型的 Call 对象。我们主要分析其余两句。
loadServiceMethod(method)
流程概要
实际上,这个方法我们在上节简单学习过了:
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;
}
当接口方法调用时,会先从缓存(一个 Map)中找,如果没有的话就新建一个,我们看到 ServiceMethod 也是用到建造者模式:
result = new ServiceMethod.Builder(this, method).build();
传入了 retrofit 对象和我们调用的方法 method 作为参数创建了一个 ServiceMethod:
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// 获取 method 中的所有注解
this.methodAnnotations = method.getAnnotations();
// 获取 method 中方法的参数类型
this.parameterTypes = method.getGenericParameterTypes();
// 获得参数的值
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
build() 方法
然后调用 build() 方法。
build() 方法内容比较多,主要是一些判断并根据 retrofit 对象的实例变量状态创建 ServiceMethod 的变量。关键是一下几行代码:
// 根据 retrofit 对象的 CallAdapterFactory 为 ServiceMethod 创建一个 callAdapter
callAdapter = createCallAdapter();
// 根据 retrofit 对象创建一个 responseConverter,默认是一个 BuildInConveter
responseConverter = createResponseConverter();
// 解析 method 的所有注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
前两句代码都很好理解,这里讲一下 parseMethodAnnotation 方法,该方法内容是许多个判断语句,这里我们看其中一个判断:
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if ...
可以看出就是对注解的类型进行判断,如果是 DELETE、POST、PATCH 等这样的 Http 请求方式,则调用 parseHttpMethodAndPath 设置 ServiceMethod 实例的 httpMethod 变量。
例如我们的接口是这样的:
@GET("/users/{user}")
Call<TestModel> repo(@Path("user") String user);
那么当我们调用 repo 方法时,会将 GET 设置到 httpMethod 中,将其值 "/users/{user}" 进一步解析,设置到 relativeUrl 中。
那么 repo(@Path("user") String user) 里的参数呢?我们之前创建 method 时获取了方法的『参数类型』和『参数值』,所以在解析过外层的想 @GET,@Headers 这样的注解后,就会进行方法参数注解的解析:
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
具体逻辑就不多说了,这里通过 Java 的 『反射机制』和『自定义注解』特性,使得我们能够通过一种简单的方式,把我们编写在 Http 接口的信息,都转化为了 ServiceMethod 所必要的变量值。
OkHttpCall
现在 ServiceMethod 创建好了,我们学习到他包含了许多东西,包括了我们要请求的地址,请求的方式以及 CallAdapter、Converter 等等。
现在我们要用他来构造一个 okHttpCall:
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
当你调用 okHttpCall 的 enqueue 方法或 execute 方法时,如果还没生成一个 okhttp3.Call 对象则会调用以下方法:
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;
}
这里创建了调用了 ServiceMethod.toRequest(args) 方法构建了一个 HTTP Request 对象:
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
可以看到就是把我们编写的信息作为参数去构建的。
这里的 Request 对象和 Call 对象实际上都属于 okHttp 的范畴了,也就是说至此我们的『Http 接口』最终转化为了一个『okhttp3.Call』并将工作交给了 okHttp 去执行。
那么 Retrofit 框架的工作也就大体完成了。
结语
实际上这节的内容并不复杂,只是细节的东西太多了,有许多 Retrofit 框架以外的内容,我自己也并不熟悉。所以其实内容比较杂乱,但是 Retrofit 的主要思想就是利用 Java 的特性,来减少了编写代码时的工作量。
这确实是一个框架设计的经典作品,值得大家去深入学习。
[旧][Android] Retrofit 源码分析之 ServiceMethod 对象的更多相关文章
- [旧][Android] Retrofit 源码分析之执行流程
备注 原发表于2016.04.23,资料已过时,仅作备份,谨慎参考 前言 由于是第一次自己翻看源代码进行学习,加上基础不好,在看源代码的过程中简直痛苦不堪,但同时也暴露出了自己的许多问题.我觉得学习源 ...
- [旧][Android] Retrofit 源码分析之 Retrofit 对象
备注 原发表于2016.04.27,资料已过时,仅作备份,谨慎参考 前言 在上一周学习了一下 Retrofit 的执行流程.接下来的文章要更为深入地学习 Retrofit 的各个类,这次我们先学习一下 ...
- Retrofit源码分析(一)
1.基本用法 创建接口 public interface GitHubService { @GET("users/{user}/repos") Observable<List ...
- Appium Android Bootstrap源码分析之启动运行
通过前面的两篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>和<Appium Android Bootstrap源码分析之命令解析 ...
- Appium Android Bootstrap源码分析之命令解析执行
通过上一篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在b ...
- Appium Android Bootstrap源码分析之控件AndroidElement
通过上一篇文章<Appium Android Bootstrap源码分析之简介>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一个初步的 ...
- Android HandlerThread 源码分析
HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我又有一 个耗时任务需要执行,我们不得不重新创建线 ...
- Android Choreographer 源码分析
Choreographer 的作用主要是配合 Vsync ,给上层 App 的渲染提供一个稳定的 Message 处理的时机,也就是 Vsync 到来的时候 ,系统通过对 Vsync 信号周期的调整, ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
随机推荐
- Rust 实现一个简单的区块链
一.背景 近期用 Rust 实现了 Jeiwan/blockchain_go,与原项目相比没有加入新的功能,只是换了一个编程语言实现了一遍,源码放在 Github 上. 开发这个项目,花费了好几个周末 ...
- 计算机视觉3-> yolov5目标检测1 |从入门到出土
本来就想着是对自己第一次跑yolov5的coco128的一个记录,没想到现在准备总结一下的时候,一方面是继续学习了一些,另一方面是学长的一些任务的要求,挖出了更多的东西,所以把名字改为了"从 ...
- 微服务架构 | 5.4 Sentinel 流控、统计和熔断的源码分析
目录 前言 1. Sentinel 的自动装配 1.2 依赖引入 1.3 SentinelWebAutoConfiguration 配置类 1.4 CommonFilter 过滤器 1.5 小结 2. ...
- 洛谷P1060 java题解
题目描述: 解题思路: 重要度相当于价值的倍率 (物品价格*重要度=价值) 经典的背包问题 直接DP把各种情况下的最优解打表出来取最后一个就行了 代码: import java.util.Scanne ...
- 同态加密与 Paillier/RSA
本文作者: wdxtub 本文链接: http://wdxtub.com/flt/flt-03/2020/12/02/ 白话同态加密 虽然同态加密即使现在听起来也很陌生,但是其实这个概念来自 1978 ...
- linux 启动过程原理哦
bios加电自检硬件设备 grub引导加载程序 当内核被加载到内存,内核阶段就开始了. init进程是所有进程的发起者和控制者.因为在任何基于unix的系统中,它都是第一个运行的进程. 然后执行sys ...
- Xml的一些基本概念(摘抄自w3school.com.cn)
一个Xml的文档示例: 点击查看代码 <?xml version="1.0" encoding="ISO-8859-1"?> <note> ...
- Td 内容不换行,超过部分自动截断,用...表示
转载请注明来源:https://www.cnblogs.com/hookjc/ <table width="200px" style="table-layout:f ...
- 将一个读取流转换成bitmap对象
将一个读取流转换成bitmap对象: BitmapFactory:可以将文件,读取流,字节数组转换成一个Bitmap对象. Bitmap bitmap = Bitma ...
- Java中命名Dao、Bean、conn等包的含义(不定期补充)
感谢大佬:https://blog.csdn.net/j904538808/article/details/78904732 (1)DAO是Data Access Object数据访问接口.数据访问: ...