Retrofit 源码阅读笔记(二)
第一篇文章中介绍了 Retrofit
的动态代理实现、方法注解解析等等内容:Android Retrofit 源码阅读笔记(一)。
本篇文章是系列文章的第二篇。
方法参数解析
在第一篇文章中介绍了 Http
请求的解析是通过 RequestFactory.Builder#build()
方法完成,在它的内部又分为两步来完成解析,通过 parseMethodAnnotation()
方法完成方法注解的解析(该方法已经在前面的文章中介绍了),通过 parseParameter()
来完成对方法参数的解析,方法参数的解析就是本节的内容。
首先我们来看看 RequestFactory.Builder#parseParameter()
方法的源码:
在请求参数中必须有 Retrofit
的参数注解修饰,而且只能有一个。有一种参数可以例外,那就是 Kotlin
协程中的 suspend
函数的最后一个参数可以不用注解修饰,那个参数固定为 Continuation
类型。你又和 Retrofit
学会了如何判断 suspend
函数😂。
我们继续看看 parseParameterAnnotation()
函数的实现,由于它的实现代码比较长,我就分不同的注解来分析:
@Url
@Url
注解表示该参数是请求的完整地址,他和方法注解中的 Path
会冲突。
这里对 @Url
注解做一个总结:
- 不能使用多个
@Url
注解。 - 与
@Path
注解冲突。 -
@Query
、@QueryName
、@QueryMap
只能在@Url
之后。 - 与方法注解中的相对地址冲突。
- 参数的类型只能是
HttpUrl
(OkHttp
中的类)、String
、URI
、Uri
(Android
中的类)其中之一。 - 参数注解处理类是
ParameterHandler.RelativeUrl
。
我们再来看看 ParameterHandler.RelativeUrl
源码:
构建 RequestBuilder
的处理方法就是 apply()
方法,在 RelativeUrl
中直接就是把参数值通过 RequestBuilder#setRelativeUrl()
方法设置进去。
@Path
@Path
就是前面一篇文章中提到的相对路径占位符。我这里举一个例子:
在执行时 Path
中的 {name}
就会被替换成 path
参数中的值。
我们来看看 @Path
注解处理的代码:
对 @Path
做一个总结:
- 定义必须在
@Query
、@QueryName
、@QueryMap
等注解之前。 - 与
@Url
注解冲突 - 在方法注解中必须定义相对路径
-
@Path
注解中的名字必须在相对路径的占位符中存在 - 获取参数类型转换成
String
类型的Converter
,这个就是一个类型转换器,很多地方都用它做类型转换,我们也可以自定义相关的ConverterFactory
来完成我们自己需要的类型转换,开发中常见的就是Json
转RequestBody
,RequestBody
转Json
,我们后面会单独分析ConverterFactory
。 - 参数处理类是
ParameterHandler.Path
。
看看 ParameterHandler.Path
的源码:
直接通过 RequestBuilder#addPathParam()
方法将相对路径的参数替换成真实的值,参数的值通过 Converter#convert()
方法转换成 String
。
@Query
@Query
注解就不用多说了,就是 Http
协议中的 Query
参数,这里注意 @Query
的 Key
是可以重复的,如果重复的话就当成是一个数组处理。
对 @Query
做一个总结:
-
@Query
可以处理数组或者迭代器类型的参数,还可以处理普通类型的参数。 - 参数最终需要转换成
String
,如果是迭代器或者数组,是他们的Element
需要转换成String
。 - 普通类型的参数处理类是
ParameterHandler.Query
,如果是迭代器和数组会调用它的iterable()
与array()
方法转换。
看看 ParameterHandler.Query
的源码:
首先将参数通过 Converter
转换成 String
,通过 RequestBuilder#addQueryParam()
方法添加到 Request
中。
@QueryName
它和 @Query
差不多,只是它的 value
值是空的,就只是指定可以空的 Query
。它的处理代码也几乎和 @Query
一样的,只是它的参数处理类换成了 ParameterHandler.QueryName
,我们直接看看它的源代码:
基本可以说和 Query
一模一样,只是 value
值设置为空。
@QueryMap
相对于 @Query
注解只能指定单个 Query
,而 @QueryMap
就可以指定多个 Query
,它的参数类型必须是 Map
。
简单总结一下:
- 参数类型必须是
Map
,Map
的Key
的类型必须是String
。 - 需要将
Map
的Value
转换成String
。 - 最后的参数处理类是
ParameterHandler.QueryMap
。
再来看看 ParameterHandler.QueryMap
的源码:
参数的 Key
,Value
都不能为空,其他的就和 @Query
类似了。
@Header
用来描述 Http
请求的 Header
,在源码中的处理方式和 @Query
注解一模一样,就不看它的源码了,看看它的参数处理类 ParameterHandler.Header
源码:
上面的代码也是朴实无华,直接调用 ReqeustBuilder#addHeader()
方法添加 Http
的 Header
。
@HeaderMap
@HeaderMap
与 @Header
的关系,就相当于 @QueryMap
与 @Query
的关系,它的处理方式和 @QueryMap
一样,源码就不看了,这里看它的参数处理类 ParameterHandler.HeaderMap
的源码实现:
也没有什么特别之处,相对于 @Header
的处理,@HeaderMap
会遍历它的所有 Key-Value
然后分别调用 RequestBuilder#addHeader()
方法。
@Field
仅限方法被 @FormUrlEncoded
修饰的方法,表示表单类型的 Body
的参数。
简单总结(和 @Query
也比较类似):
- 方法必须有
@FormUrlEncoded
修饰。 - 处理迭代器类型和数组类型,生成的参数处理器通过
Parameterhandelr.Field
的iterable()
和array()
方法处理后生成的处理器,他们的Converter
也都是迭代器或者数组的泛型类型转String
。 - 除了数组和迭代器的其他的普通类型,直接用当前类型转
String
的Converter
,使用的参数处理器是Parameterhandelr.Field
。
继续看看 Parameterhandelr.Field
的源码实现:
跳过空的数据,通过 RequestBuilder#addFormField()
将表单 Feild
参数添加到 Request
中。
@FeildMap
@FeildMap
与 @Feild
的关系,就相当于 @QueryMap
与 @Query
的关系,处理逻辑和 @QueryMap
类似(只是有 @FormUrlEncoded
限制),源代码就贴了,它的参数处理类是 ParameterHandler.FieldMap
,看看它的源码:
和 Parameterhandelr.Field
相比就是会遍历每个 Map
中的 Key-Value
,然后分别调用 RequestBuilder#addFormField()
方法添加 Field
参数。
@Part
方法中必须有 @Multipart
修饰的方法才能,使用带 @Part
注解的参数,他表示是 Multipart
类型 RequestBody
中的一部分。
@Part
和 @Query
注解一样都是单独处理数组和迭代器类型的数据的,处理方式也类似,后面的描述也就不分析了,由于 Part
类型是必须要一个 name
,这个 name
可以从 @Part
注解中获取,也可以从参数类型为 MultipartBody.Part
中获取。所以代码就分为了 @Part
中的 name
为空和不为空两种处理逻辑。
@Part
中的 name
为空:
- 参数类型必须是
MultipartBody.Part
。 - 参数处理器是
ParameterHandler.RawPart
,使用的是一个单例对象。
@Part
中的 name
不为空:
- 参数类型不能是
MultipartBody.Part
。 - 将
@Part
中的name
和encoding
参数构建成一个新的Headers
。 - 需要找到一个将参数类型转换成
RequestBody
的Converter
。 - 参数处理器是
ParameterHandler.Part
。
我们先看看 name
不为空的参数处理器 ParameterHandler.RawPart
的源码:
直接通过 RequestBuilder#addPart()
方法添加 Part
。
然后再看看 name
为空的参数处理器 ParameterHandelr.Part
的源码:
和 RawPart
中不同的是,首先会通过 Converter
将参数转换成 RequestBody
,然后调用 RequestBuilder#addPart()
方法,注意这里还把之前生成的 Headers
也传递进去了,它会作为 MultipartBody.Part
中的 Headers
。
@PartMap
@PartMap
与 @Part
的关系,就相当于 @QueryMap
与 @Query
的关系,跳过源码分析了,直接看看它的参数处理器的类 ParameterHandler.PartMap
。
和 @Part
相比,也就是添加多个 Part Body
到 RequestBuilder
中。
@Body
描述请求的 Request Body
,源码如下:
简单描述一下上面的代码:
- 方法不能有
@Multipart
或者@FormUrlEncoded
注解修饰。 - 不能添加多个
Body
。 - 查找
Request Body
的Converter
。 - 参数处理器是
ParameterHandler.Body
。
再看看 ParameterHandler.Body
的源码:
我自己都不想解释了,代码很简单,自己看。。。
最后
终于看完了方法参数解析的代码,很多的代码都是一些模版代码,阅读起来很简单,到这里 RequestFactory
的代码也就分析完了,也就是 Request
的解析,其中包括方法的解析和方法参数的解析。
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。