整體Retrofit內(nèi)容如下:
- 1、Retrofit解析1之前哨站——理解RESTful
- 2缕减、Retrofit解析2之使用簡介
- 3乔宿、Retrofit解析3之反射
- 4祈秕、Retrofit解析4之注解
- 5茄袖、Retrofit解析5之代理設計模式
- 6操软、Retrofit解析6之面向接口編程
- 7、Retrofit解析7之相關類解析
- 8宪祥、Retrofit解析8之核心解析——ServiceMethod及注解1
- 9聂薪、Retrofit解析8之核心解析——ServiceMethod及注解2
- 10、Retrofit解析9之流程解析
- 11蝗羊、Retrofit解析10之感謝
上篇文章已經(jīng)介紹了Retrofit里面的大多數(shù)類藏澳,今天就重點介紹ServiceMethod,本片文章主要內(nèi)容如下:
- 1耀找、ParameterHandler類
- 2翔悠、ServiceMethod類
- 3、Retrofit類
為了方便后面學習ServiceMethod涯呻,我們先來看下ParameterHandler類
一凉驻、ParameterHandler類
(一)腻要、ParameterHandler類是什么复罐?
從字面意思我們猜到 他是參數(shù)處理類。具體是不是那雄家?用源碼說話效诅,看源碼
abstract class ParameterHandler<T> {
abstract void apply(RequestBuilder builder, T value) throws IOException;
final ParameterHandler<Iterable<T>> iterable() {
return new ParameterHandler<Iterable<T>>() {
@Override void apply(RequestBuilder builder, Iterable<T> values) throws IOException {
if (values == null) return; // Skip null values.
for (T value : values) {
ParameterHandler.this.apply(builder, value);
}
}
};
}
final ParameterHandler<Object> array() {
return new ParameterHandler<Object>() {
@Override void apply(RequestBuilder builder, @Nullable Object values) throws IOException {
if (values == null) return; // Skip null values.
for (int i = 0, size = Array.getLength(values); i < size; i++) {
//noinspection unchecked
ParameterHandler.this.apply(builder, (T) Array.get(values, i));
}
}
};
}
//由于篇幅的限制,我沒有粘貼靜態(tài)內(nèi)部類
}
通過簡單閱讀源代碼,我們得知:
- ParameterHandler 是一個抽象類
- 一共有3個方法乱投,其中一個是抽象方法
- apply(RequestBuilder builder, T value)方法是抽象的咽笼,需要子類去實現(xiàn)
- iterable()方法返回的是new的一個ParameterHandler,不過的他的泛型是Iterable<T>戚炫,所以它的抽象方法的具體實現(xiàn)是遍歷這個迭代器Iterable剑刑,調(diào)用這個對象的apply()方法,注意這個方法是final双肤。
- array()方法 返回的是一個new的一個ParameterHandler施掏,它的泛型是Object,而他的抽象方法是通過遍歷數(shù)組的方式來分別調(diào)用對應的apply()方法茅糜,這個方法也是final的
這里說下iterable()方法和array()方法七芭,說明ParameterHandler是支持兩種數(shù)據(jù)類型的處理方式。一個是對應迭代器(iterable)的類型;一個是對應的是數(shù)組類型的蔑赘。所以我們繼續(xù)往下看
(二)狸驳、靜態(tài)內(nèi)部類RelativeUrl
看下源碼:
static final class RelativeUrl extends ParameterHandler<Object> {
@Override void apply(RequestBuilder builder, Object value) {
builder.setRelativeUrl(value);
}
}
- 首先,這個類是final的
- 其次缩赛,貌似很簡單耙箍,RelativeUrl類內(nèi)部就是調(diào)用RequestBuilder的setRelativeUrl方法來設置RequestBuilder的relativeUrl的值
(三)、靜態(tài)內(nèi)部類Header
看下源碼:
static final class Header<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
Header(String name, Converter<T, String> valueConverter) {
this.name = checkNotNull(name, "name == null");
this.valueConverter = valueConverter;
}
@Override void apply(RequestBuilder builder, T value) throws IOException {
if (value == null) return; // Skip null values.
builder.addHeader(name, valueConverter.convert(value));
}
}
- 首先這個類是final的
- 其次這個類有兩個final的變量峦筒,在構造的時候賦值
- apply抽象方法也是調(diào)用RequestBuilder的addHeader方法給RequestBuilder的header賦值
- apply里面有調(diào)用Converter的convert的方法把value進行轉化究西,其實是序列化。
(四)物喷、靜態(tài)內(nèi)部類Path
看下源碼:
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Path(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = checkNotNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, T value) throws IOException {
if (value == null) {
throw new IllegalArgumentException(
"Path parameter \"" + name + "\" value must not be null.");
}
builder.addPathParam(name, valueConverter.convert(value), encoded);
}
- 首先這個類是final的
- 其次這個類有三個final的變量卤材,在構造的時候賦值
- apply抽象方法也是調(diào)用RequestBuilder的addPathParam方法進行設置
- apply里面有調(diào)用Converter的convert的方法把value進行轉化,其實是序列化峦失。
(五)扇丛、靜態(tài)內(nèi)部類Query
看下源碼:
static final class Query<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Query(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = checkNotNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, T value) throws IOException {
if (value == null) return; // Skip null values.
builder.addQueryParam(name, valueConverter.convert(value), encoded);
}
}
- 首先這個類是final的
- 其次這個類有三個final的變量,在構造的時候賦值
- apply抽象方法也是調(diào)用RequestBuilder的addQueryParam方法進行設置
- apply里面有調(diào)用Converter的convert的方法把value進行轉化尉辑,其實是序列化帆精。
(六)、靜態(tài)內(nèi)部類QueryName
看下源碼:
static final class QueryName<T> extends ParameterHandler<T> {
private final Converter<T, String> nameConverter;
private final boolean encoded;
QueryName(Converter<T, String> nameConverter, boolean encoded) {
this.nameConverter = nameConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, T value) throws IOException {
if (value == null) return; // Skip null values.
builder.addQueryParam(nameConverter.convert(value), null, encoded);
}
}
- 首先這個類是final的
- 其次這個類有二個final的變量隧魄,在構造的時候賦值
- apply抽象方法也是調(diào)用RequestBuilder的addQueryParam方法進行設置
- apply里面有調(diào)用Converter的convert的方法把value進行轉化卓练,其實是序列化。
(七)购啄、靜態(tài)內(nèi)部類QueryMap
看下源碼:
static final class QueryMap<T> extends ParameterHandler<Map<String, T>> {
private final Converter<T, String> valueConverter;
private final boolean encoded;
QueryMap(Converter<T, String> valueConverter, boolean encoded) {
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, Map<String, T> value) throws IOException {
if (value == null) {
throw new IllegalArgumentException("Query map was null.");
}
for (Map.Entry<String, T> entry : value.entrySet()) {
String entryKey = entry.getKey();
if (entryKey == null) {
throw new IllegalArgumentException("Query map contained null key.");
}
T entryValue = entry.getValue();
if (entryValue == null) {
throw new IllegalArgumentException(
"Query map contained null value for key '" + entryKey + "'.");
}
builder.addQueryParam(entryKey, valueConverter.convert(entryValue), encoded);
}
}
}
- 首先這個類是final的
- 其次這個類有二個final的變量襟企,在構造的時候賦值
- apply抽象方法內(nèi)部首先遍歷Map,獲取對應的key 和value,然后調(diào)用RequestBuilder的addQueryParam方法進行設置
- apply里面有調(diào)用Converter的convert的方法把value進行轉化狮含,其實是序列化顽悼。
(八)曼振、靜態(tài)內(nèi)部類HeaderMap
看下源碼:
static final class HeaderMap<T> extends ParameterHandler<Map<String, T>> {
private final Converter<T, String> valueConverter;
HeaderMap(Converter<T, String> valueConverter) {
this.valueConverter = valueConverter;
}
@Override void apply(RequestBuilder builder, Map<String, T> value) throws IOException {
if (value == null) {
throw new IllegalArgumentException("Header map was null.");
}
for (Map.Entry<String, T> entry : value.entrySet()) {
String headerName = entry.getKey();
if (headerName == null) {
throw new IllegalArgumentException("Header map contained null key.");
}
T headerValue = entry.getValue();
if (headerValue == null) {
throw new IllegalArgumentException(
"Header map contained null value for key '" + headerName + "'.");
}
builder.addHeader(headerName, valueConverter.convert(headerValue));
}
}
}
- 首先這個類是final的
- 其次這個類有一個final的變量,在構造的時候賦值
- apply抽象方法內(nèi)部首先遍歷Map,獲取對應的key 和value蔚龙,然后調(diào)用RequestBuilder的addHeader方法進行設置
- apply里面有調(diào)用Converter的convert的方法把value進行轉化冰评,其實是序列化。
(九)木羹、靜態(tài)內(nèi)部類Field
看下源碼:
static final class Field<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Field(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = checkNotNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, T value) throws IOException {
if (value == null) return; // Skip null values.
builder.addFormField(name, valueConverter.convert(value), encoded);
}
}
- 首先這個類是final的
- 其次這個類有三個final的變量甲雅,在構造的時候賦值
- apply抽象方法內(nèi)部調(diào)用RequestBuilder的addFormField方法進行設置
- apply里面有調(diào)用Converter的convert的方法把value進行轉化,其實是序列化坑填。
(十)务荆、靜態(tài)內(nèi)部類FieldMap
看下源碼:
static final class FieldMap<T> extends ParameterHandler<Map<String, T>> {
private final Converter<T, String> valueConverter;
private final boolean encoded;
FieldMap(Converter<T, String> valueConverter, boolean encoded) {
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, Map<String, T> value) throws IOException {
if (value == null) {
throw new IllegalArgumentException("Field map was null.");
}
for (Map.Entry<String, T> entry : value.entrySet()) {
String entryKey = entry.getKey();
if (entryKey == null) {
throw new IllegalArgumentException("Field map contained null key.");
}
T entryValue = entry.getValue();
if (entryValue == null) {
throw new IllegalArgumentException(
"Field map contained null value for key '" + entryKey + "'.");
}
builder.addFormField(entryKey, valueConverter.convert(entryValue), encoded);
}
}
}
- 首先這個類是final的
- 其次這個類有二個final的變量越驻,在構造的時候賦值
- apply抽象方法內(nèi)部首先遍歷Map,獲取對應的key 和value紧武,然后調(diào)用RequestBuilder的addFormField方法進行設置
- apply里面有調(diào)用Converter的convert的方法把value進行轉化,其實是序列化耍目。
(十一)蚪黑、靜態(tài)內(nèi)部類Part
看下源碼:
static final class Part<T> extends ParameterHandler<T> {
private final Headers headers;
private final Converter<T, RequestBody> converter;
Part(Headers headers, Converter<T, RequestBody> converter) {
this.headers = headers;
this.converter = converter;
}
@Override void apply(RequestBuilder builder, T value) {
if (value == null) return; // Skip null values.
RequestBody body;
try {
body = converter.convert(value);
} catch (IOException e) {
throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
}
builder.addPart(headers, body);
}
}
- 首先這個類是final的
- 其次這個類有二個final的變量盅惜,在構造的時候賦值
- apply抽象方法內(nèi)部首先通過convert的方法把value進行轉化(其實是序列化),然后調(diào)用RequestBuilder的addPart方法進行設置忌穿。
(十二)抒寂、靜態(tài)內(nèi)部類RawPart
看下源碼:
static final RawPart INSTANCE = new RawPart();
private RawPart() {
}
@Override void apply(RequestBuilder builder, MultipartBody.Part value) throws IOException {
if (value != null) { // Skip null values.
builder.addPart(value);
}
}
- 首先這個類是final的
- 其次這個類構造函數(shù)是私有的,并且定義了一個靜態(tài)的常量對象掠剑,所以這個是類的對象是單例模式屈芜。
- apply抽象方法調(diào)用RequestBuilder的addPart方法進行設置。
(十三)朴译、靜態(tài)內(nèi)部類PartMap
看下源碼:
static final class PartMap<T> extends ParameterHandler<Map<String, T>> {
private final Converter<T, RequestBody> valueConverter;
private final String transferEncoding;
PartMap(Converter<T, RequestBody> valueConverter, String transferEncoding) {
this.valueConverter = valueConverter;
this.transferEncoding = transferEncoding;
}
@Override void apply(RequestBuilder builder, Map<String, T> value) throws IOException {
if (value == null) {
throw new IllegalArgumentException("Part map was null.");
}
for (Map.Entry<String, T> entry : value.entrySet()) {
String entryKey = entry.getKey();
if (entryKey == null) {
throw new IllegalArgumentException("Part map contained null key.");
}
T entryValue = entry.getValue();
if (entryValue == null) {
throw new IllegalArgumentException(
"Part map contained null value for key '" + entryKey + "'.");
}
Headers headers = Headers.of(
"Content-Disposition", "form-data; name=\"" + entryKey + "\"",
"Content-Transfer-Encoding", transferEncoding);
builder.addPart(headers, valueConverter.convert(entryValue));
}
}
}
- 首先這個類是final的
- 其次這個類有二個final的變量井佑,在構造的時候賦值
- apply抽象方法內(nèi)部首先遍歷Map,獲取對應的key 和value,然后調(diào)用okhttp3的Headers.of()方法添加headers眠寿,最后調(diào)用RequestBuilder的addPart方法進行設置
- apply里面有調(diào)用Converter的convert的方法把value進行轉化躬翁,其實是序列化。
(十四)盯拱、靜態(tài)內(nèi)部類Body
看下源碼:
static final class Body<T> extends ParameterHandler<T> {
private final Converter<T, RequestBody> converter;
Body(Converter<T, RequestBody> converter) {
this.converter = converter;
}
@Override void apply(RequestBuilder builder, T value) {
if (value == null) {
throw new IllegalArgumentException("Body parameter value must not be null.");
}
RequestBody body;
try {
body = converter.convert(value);
} catch (IOException e) {
throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
}
builder.setBody(body);
}
}
- 首先這個類是final的
- 其次這個類有一個final的變量盒发,在構造的時候賦值
- apply抽象方法內(nèi)部首先調(diào)用Converter的convert的方法把value進行轉化,其實就是序列化狡逢。然后調(diào)用RequestBuilder的setBody方法進行設置
(十五)宁舰、總結
本來這篇內(nèi)容應該放到上一篇文章中,但是由于受到篇幅限制的原由奢浑,所以放到這里了蛮艰,看完ParameterHandler類的源碼在回想下ParameterHandler的源碼,大家有沒有想到一些東西?
其實Retrofit團隊是這樣分工的殷费,RequestBuilder僅僅是一個包裝類印荔,但是具體的賦值操作等其實是通過ParameterHandler對應的靜態(tài)內(nèi)部類來實現(xiàn)的,這樣實現(xiàn)了包裝和操作分離详羡,實現(xiàn)了解耦仍律。多么"優(yōu)雅"的設計。
二实柠、ServiceMethod類
(一)水泉、ServiceMethod類是什么?
首先用類注解解釋下ServiceMethod是什么窒盐?
/** Adapts an invocation of an interface method into an HTTP call. */
我翻譯一下:
將一個接口的方方法的調(diào)用適配成一個HTTP的Call草则。
我的理解是:
ServiceMethod是一個負責轉化(適配)的類,負責把一個接口的抽象方法的執(zhí)行過程的結果轉化(適配)成一個網(wǎng)絡請求(HTTP call)蟹漓。
ok 那我們來看下他的源碼
(二)炕横、ServiceMethod類源碼
由于這個類略大,一直在糾結要不要把全部代碼粘貼上葡粒,但是考慮到大家的情況份殿,還是粘貼了,不過省略了Buidler內(nèi)部靜態(tài)類的代碼嗽交,希望大家理解卿嘲。
1、ServiceMethod源碼:
/** Adapts an invocation of an interface method into an HTTP call. */
final class ServiceMethod<R, T> {
// Upper and lower characters, digits, underscores, and hyphens, starting with a character.
static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);
final okhttp3.Call.Factory callFactory;
final CallAdapter<R, T> callAdapter;
private final HttpUrl baseUrl;
private final Converter<ResponseBody, R> responseConverter;
private final String httpMethod;
private final String relativeUrl;
private final Headers headers;
private final MediaType contentType;
private final boolean hasBody;
private final boolean isFormEncoded;
private final boolean isMultipart;
private final ParameterHandler<?>[] parameterHandlers;
ServiceMethod(Builder<R, T> builder) {
this.callFactory = builder.retrofit.callFactory();
this.callAdapter = builder.callAdapter;
this.baseUrl = builder.retrofit.baseUrl();
this.responseConverter = builder.responseConverter;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.headers = builder.headers;
this.contentType = builder.contentType;
this.hasBody = builder.hasBody;
this.isFormEncoded = builder.isFormEncoded;
this.isMultipart = builder.isMultipart;
this.parameterHandlers = builder.parameterHandlers;
}
/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
//省略了Builder內(nèi)部類
}
通過錯略看過源代碼可以獲取如下信息:
- ServiceMethod是final的夫壁,不能被繼承
- ServiceMethod的構造函數(shù)既不是public拾枣,也是private的,所以只能在包內(nèi)創(chuàng)建ServiceMethod對象盒让。
- ServiceMethod有一個靜態(tài)內(nèi)部類Builder梅肤。是final的,同樣不能被繼承邑茄,看到Builder凭语,大家一定知道了ServiceMethod是Builder模式,所以 ServiceMethod構造函數(shù)就一個入?yún)⑹荁uilder
- 所有的變量均是final的
- 一共有4個方法撩扒,兩個靜態(tài)的兩個非靜態(tài)的
- boxIfPrimitive (Class<?> type) 靜態(tài)方法:明顯就是獲取8個基本類型對應的裝箱類
- parsePathParameters (String path) 靜態(tài)方法: 保證url中的路徑參數(shù)(PathParameters)有且僅使用一次似扔,不會出現(xiàn)重復的路徑參數(shù)(PathParameters)。
2搓谆、源碼詳解:
2.1炒辉、變量解析:
大家先來看下變量,由于變量不多泉手,我直接上注釋了黔寇。
//用來校驗的常量
static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);
//網(wǎng)絡請求call的工廠,注釋是okhttp3的
final okhttp3.Call.Factory callFactory;
// 網(wǎng)絡請求適配器
final CallAdapter<R, T> callAdapter;
//基礎HttpUrl
private final HttpUrl baseUrl;
//響應轉化器
private final Converter<ResponseBody, R> responseConverter;
//網(wǎng)絡請求的方法
private final String httpMethod;
//網(wǎng)絡請求url的相對路徑
private final String relativeUrl;
// 請求頭
private final Headers headers;
//請求類型
private final MediaType contentType;
//是否有請求體
private final boolean hasBody;
//是否是表單提交
private final boolean isFormEncoded;
//以二進制流的方式提交
private final boolean isMultipart;
//參數(shù)處理器斩萌。具體在后面講解
private final ParameterHandler<?>[] parameterHandlers;
2.2缝裤、ServiceMethod的兩個非靜態(tài)方法:
2.2.1首先來看下toRequest方法
/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
//new 了一個RequestBuilder對象
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
//獲取入?yún)⒌膫€數(shù)
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
//遍歷入?yún)? for (int p = 0; p < argumentCount; p++) {
//配置對應的requestBuilder屬性
handlers[p].apply(requestBuilder, args[p]);
}
//調(diào)用requestBuilder的build()方法獲取一個okhttp3.request對象
return requestBuilder.build();
}
通過注釋我們發(fā)現(xiàn)屏轰,其實這個方法是:
通過方法的入?yún)韷驅⒁粋€HTTP的請求。
方法內(nèi)的大體流程如下:
- 1憋飞、new 了一個RequestBuilder對象
- 2霎苗、 獲取入?yún)⒌膫€數(shù),并與自身對應的入?yún)⑻幚眍惖膫€數(shù)進行對比榛做,不一致拋異常唁盏,理論上應該是一致的,因為你的每個“方法”的每一個入?yún)⒍紝粋€注解检眯。
- 3厘擂、遍歷這些入?yún)⑻幚眍悾_始配置requestBuilder屬性
- 4锰瘸、配置完畢刽严,調(diào)用requestBuilder的.build()方法獲取一個okhttp3.Request對象
2.2.2 讓我們來看下toResponse方法
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
通過方法注釋我們得知:
通過一個HTTP的響應體(response body)來構建一個方法的返回值。
方法很簡單就一行避凝,就是調(diào)用響應解析/處理器(responseConverter)的convert方法來解析/反序列化響應體港庄。
3、靜態(tài)內(nèi)部類 ServiceMethod.Builder
3.1恕曲、先看下類的注釋
/**
* Inspects the annotations on an interface method to construct a reusable service method. This
* requires potentially-expensive reflection so it is best to build each service method only once
* and reuse it. Builders cannot be reused.
*/
翻譯一下就是
通過檢查接口方法上的注解來構建一個可以重用的 "服務方法"鹏氧,由于用到高性能消耗的反射,所以最好一個 "服務方法" 只構建一次佩谣,并且可以重復使用把还。Builders不能被重用
我的理解是:
其實Retrofit內(nèi)部是通過動態(tài)代理,內(nèi)部是通過反射實現(xiàn)的茸俭,大家都知道反射其實是很消耗性能的吊履,為了避免這種高性能的消耗,反射成功以后就緩存起來调鬓,下次如果有用到就重用艇炎,如果不能重用則用反射構建出來,但是Builder是不能重用的腾窝。
3.2缀踪、 ServiceMethod.Builder源碼解析
由于Builder源碼太多了,我就不上源碼了
我們來看下Builder類的變量虹脯,我直接標注釋了
3.2.1驴娃、ServiceMethod.Builder變量解析
//Retrofit
final Retrofit retrofit;
//定義接口的抽象方法
final Method method;
//定義接口的抽象方法上面的注解數(shù)組
final Annotation[] methodAnnotations;
//注解二維數(shù)組:第一維對應參數(shù)序列,第二維對應注解序列循集;
final Annotation[][] parameterAnnotationsArray;
// 參數(shù)類型數(shù)組
final Type[] parameterTypes;
// 響應的類型
Type responseType;
//是否有用 @Field 或者FieldMap 注解
boolean gotField;
//是否 有用@Part 或者 @PartMap注解
boolean gotPart;
//是否有用 @body注解
boolean gotBody;
// 是否 有用@Path 注解
boolean gotPath;
//是否 有用@Query 或者@QueryName 注解
boolean gotQuery;
// 是否具有url
boolean gotUrl;
//http 請求的方法
String httpMethod;
//是否 需要請求體
boolean hasBody;
//是否 使用@FormUrlEncoded 注解
boolean isFormEncoded;
// 是否 使用了@Multipart 注解
boolean isMultipart;
// 相對路徑
String relativeUrl;
//請求頭
Headers headers;
//類型
MediaType contentType;
//相對路徑的url參數(shù)
Set<String> relativeUrlParamNames;
//參數(shù)處理數(shù)組
ParameterHandler<?>[] parameterHandlers;
//響應轉化器
Converter<ResponseBody, T> responseConverter;
//請求轉化器
CallAdapter<T, R> callAdapter;
3.2.2唇敞、 Builder構造函數(shù)解析
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
- 我們知道Builder的構造函數(shù)有兩個入?yún)ⅲ粋€是Retrofit對象本身,一個是Method 對象疆柔。
- methodAnnotations咒精、parameterTypes、parameterAnnotationsArray這三個變量的值都是通過method的三個方法獲取的
PS:通過之前反射的學習我們知道Method.getAnnotations(); 是獲取方法上面對應的注解旷档。method.getGenericParameterTypes();獲取的是方法參數(shù)的類型模叙,里面帶有實際的參數(shù)類型。method.getParameterAnnotations()彬犯;獲取的是方法參數(shù)上面的注解,是一個二維數(shù)組查吊,第一個維度代表的是方法參數(shù)對應的下標谐区,比如,一個方法有3個參數(shù)逻卖,那0代表第一個參數(shù)宋列,1代表第二個參數(shù),2代表第三個參數(shù)评也。
通過構造函數(shù)我們知道Builder類里面的5個變量已經(jīng)被初始化了
3.2.3炼杖、 Builder的方法build()方法解析
public ServiceMethod build() {
//通過調(diào)用createCallAdapter()方法獲取callAdapter
callAdapter = createCallAdapter();
//通過調(diào)用callAdapter的responseType方法獲取響應體類型
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
//通過createResponseConverter()方法獲取響應轉化器
responseConverter = createResponseConverter();
//遍歷方法的注解
for (Annotation annotation : methodAnnotations) {
//解析方法注解
parseMethodAnnotation(annotation);
}
//如果沒有HTTP請求的方法,則拋異常
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
//在上面遍歷方法注解并解析方法注解的時候盗迟,hasBody坤邪,isMultipart,isFormEncoded等已經(jīng)完成賦值
//如果沒有請求體
if (!hasBody) {
//但是 還是使用了@Multipart注解罚缕,使用了@Multipart 是一定有消息體的
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
//但是 還是使用了 @FormEncoded艇纺,使用了@FormEncoded 是一定有消息體的
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
//如果方法 入?yún)⒌淖⒔猓拈L度也就是有多少個入?yún)⒂实闅v每個入?yún)淖⒔? int parameterCount = parameterAnnotationsArray.length;
//創(chuàng)建對應的數(shù)量的參數(shù)處理類數(shù)組黔衡,這時候開始處理入?yún)淖⒔饬? parameterHandlers = new ParameterHandler<?>[parameterCount];
//遍歷入?yún)⒌臄?shù)量
for (int p = 0; p < parameterCount; p++) {
//首先獲取入?yún)⒌念愋? Type parameterType = parameterTypes[p];
//如果是不能處理的類型,則拋異常
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
//如果是可以處理的類型獲取對應位置入?yún)⒌淖⒔? Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
//如果對應入?yún)⒌淖⒔鉃閚ull腌乡,則拋異常
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
//如果對應的入?yún)⒆⒔獠粸閚ull,則調(diào)用parseParameter方法獲取ParameterHandler盟劫,cancel在這里方法里面創(chuàng)建ParameterHandler里面的靜態(tài)內(nèi)部類,后面咱們再仔細看下与纽。
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
//for循環(huán)遍歷以后侣签,如果relativeUrl還為null,同時沒有使用急迂!@url注解硝岗,這樣的話我們就無法獲取url地址了,所以要拋異常袋毙。
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
//如果沒有使用@FormEncoded注解型檀、@Multipart和,同時HTTP請求方式也不需要響應提听盖,但是卻使用了@Body 注解胀溺,這不是自相矛盾裂七,所以拋異常。
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
//如果使用@FormEncoded注解仓坞,卻沒使用@Field注解背零,不符合規(guī)范,則拋異常
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
//如果使用 @Multipart 注解无埃,卻沒有使用@Part 注解徙瓶,拋異常
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
//走到這里說明沒有問題,則new一個ServiceMethod對象嫉称,入?yún)⑹荁uilder自己侦镇,然后return這個ServiceMethod對象。
return new ServiceMethod<>(this);
}
大體流程如下:
- 1织阅、通過createCallAdapter()方法取得callAdapter對象
- 2壳繁、通過callAdapter的responseType()方法獲取響應對應的類型
- 3、通過createResponseConverter()來獲取響應轉化/解析器
- 4荔棉、遍歷方法上的注解闹炉,并解析注解
- 5、判斷是GET還是POST或者其他HTTP請求方式
- 6润樱、異常邏輯判斷渣触,如果HTTP請求方式不需要消息體,但是卻使用@FormEncoded注解壹若、@Multipart昵观,自相矛盾。
- 7舌稀、獲取這個方法入?yún)⒌膫€數(shù)
- 8啊犬、遍歷這個方法的各個入?yún)ⅲ紫扰袛嗍遣皇悄芴幚淼念愋捅诓椋绻悄芴幚淼念愋途踔粒蝗缓螳@取這個入?yún)淖⒔猓蛔詈笳{(diào)用parseParameter()來獲取對應的parameterHandlers睡腿。
- 9语御、異常邏輯判斷,比如如果沒有相對路徑還沒有使用@Url席怪,我們會無法得到具體的地址应闯。
- 10、異常邏輯處理挂捻,既沒有使用@FormUrlEncode 注解也沒有使用@Multipart注解碉纺,且HTTP請求方式也需要使用請求,卻使用@Body 注解,違反規(guī)范骨田,拋異常
- 11耿导、異常邏輯處理,使用@FormUrlEncoded注解态贤,但是卻沒有使用@Field注解舱呻,這是違背Retrofit的規(guī)定的。
- 12悠汽、異常邏輯處理箱吕,使用了@Multipart 注解,卻沒有使用@Part注解柿冲,同樣是違背Retrofit的規(guī)定的茬高。
- 13、最后new了一個ServiceMethod對象姻采,并return
自此整個build流程已經(jīng)全部解析完畢雅采,那我們在看來里面設計的其它方法
3.2.4爵憎、 Builder的方法createCallAdapter解析
通過方法字面的含義我們理解為這個方法創(chuàng)建CallAdatper慨亲,具體我們看下源碼:
private CallAdapter<T, R> createCallAdapter() {
//獲取方法的返回值類型
Type returnType = method.getGenericReturnType();
//如果方法的返回值類型我們無法處理,則拋出異常
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
//如果方法是void的宝鼓,則沒有返回值
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
//獲取方法的注解
Annotation[] annotations = method.getAnnotations();
try {
//調(diào)用retrofit的callAdapter來獲取一個CallAdapter對象
//noinspection unchecked
return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
大體流程如下:
- 1刑棵、通過method.getGenericReturnType()獲取返回值類型
- 2、排除我們不能處理的類型
- 3愚铡、排除無返回值(void)的情況
- 4蛉签、調(diào)用 retrofit的callAdapter的方法來獲取一個CallAdapter對象,這個里面的內(nèi)部調(diào)用沥寥,我們一會再說碍舍。
3.2.5、 Builder的方法parseMethodAnnotation解析
這個方法看字面的意思就知道是一個處理方法的注解,具體以源碼為準邑雅。
private void parseMethodAnnotation(Annotation annotation) {
//如果是DELETE注解 代表DELETE請求
if (annotation instanceof DELETE) {
//調(diào)用parseHttpMethodAndPath()
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
//如果是GET 注解片橡,則代表是get請求
} else if (annotation instanceof GET) {
//調(diào)用parseHttpMethodAndPath()
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
//如果是 HEAD注解,則代表HEAD請求淮野,這里不是請求頭,HEAD方法跟GET方法相同捧书,只不過服務器響應時不會返回消息體。
} else if (annotation instanceof HEAD) {
//調(diào)用parseHttpMethodAndPath()
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
if (!Void.class.equals(responseType)) {
//HEAD請求是沒有響應體的
throw methodError("HEAD method must use Void as response type.");
}
//如果是PATCH請求
} else if (annotation instanceof PATCH) {
//調(diào)用parseHttpMethodAndPath()
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
//調(diào)用parseHttpMethodAndPath()
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
//如果是PUT請求
} else if (annotation instanceof PUT) {
//調(diào)用parseHttpMethodAndPath()
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
//如果是 OPTIONS注解骤星,代表OPTIONS請求
} else if (annotation instanceof OPTIONS) {
//調(diào)用parseHttpMethodAndPath()
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
//如果是@HTTP 注解经瓷,則代表自定義 HTTP請求
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
//調(diào)用parseHttpMethodAndPath()
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
//如果是 注解Headers 則代表請求頭
} else if (annotation instanceof retrofit2.http.Headers) {
//獲取對應的String 數(shù)組
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
//判斷數(shù)組長度
if (headersToParse.length == 0) {
//這里可見如果一定使用@Headers 注解,則一定要有內(nèi)容洞难,否則會報錯舆吮。
throw methodError("@Headers annotation is empty.");
}
//調(diào)用parseHeaders()方法來解析headersToParse
headers = parseHeaders(headersToParse);
//如果 是Multipart注解,則代表用文件提交
} else if (annotation instanceof Multipart) {
//如果是文件提交,則不能使用表單提交歪泳,這是互斥的
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
//給isMultipart賦值為true
isMultipart = true;
//如果 是 FormUrlEncoded注解萝勤,則代表表單提交
} else if (annotation instanceof FormUrlEncoded) {
由于和文件提交互斥,要判斷
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
//給isFormEncoded賦值
isFormEncoded = true;
}
}
這個沒什么流程呐伞,就是不斷if else的過程敌卓,其中先是判斷注解的類型,根據(jù)不同的類型來調(diào)用parseHttpMethodAndPath方法伶氢,這里主要指的是DELETE趟径、GET、HEAD癣防、PATCH蜗巧、POST、PUT蕾盯、OPTIONS請求幕屹,當然也包括自定義的HTTP請求。然后判斷如果Headers级遭,則說明要向請求頭里面添加數(shù)據(jù)望拖。最后做了表單提交和二進制流提交的互斥。那我們接來下就來看下對應的parseHttpMethodAndPath()方法挫鸽。
3.2.6说敏、 Builder的方法parseHttpMethodAndPath解析
通過方法名,我們理解這個方法主要是"解析HTTP的請求方法和路徑"丢郊,我們一起來看下源碼
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
//因為Http請求只能設置一種請求方式盔沫,所以先做判斷是否已經(jīng)設置過請求方式,如果設置過則不能重復設置
if (this.httpMethod != null) {
throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
}
//設置請求方式
this.httpMethod = httpMethod;
//根據(jù)不同的請求方式來設置是否需要請求體
this.hasBody = hasBody;
//如果注解中沒有內(nèi)容枫匾,則返回架诞,因為不需要處理
if (value.isEmpty()) {
return;
}
//如果在注解的值中存在查詢字段,則
// Get the relative URL path and existing query string, if present.
//獲取注解的值中存在'?'字符的位置
int question = value.indexOf('?');
//如果存在干茉,且不是在最后面
if (question != -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
//確保這個查詢字符沒有對應的參數(shù)
//獲取'?'字符后面的字符串
String queryParams = value.substring(question + 1);
//根據(jù)正則表達來匹配
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError("URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
//把value設置為相對路徑的值
this.relativeUrl = value;
//最后調(diào)用parsePathParameters方法來放置出現(xiàn)重復參數(shù)
this.relativeUrlParamNames = parsePathParameters(value);
}
大體流程如下:
- 1谴忧、保證請求方式只設置一次
- 2、設置請求方式和是否需要請求體
- 3等脂、如果value值為空則return
- 4俏蛮、判斷value是否有 '?' 查詢字段,有的則根據(jù)正則表達來判斷上遥,如果是符合PARAM_URL_REGEX規(guī)則則拋異常搏屑。
3.2.7、 Builder的方法parseHeaders解析
這個方法看方法名粉楚,可以理解解析請求頭
private Headers parseHeaders(String[] headers) {
//new 了一個Headers.Builder()對象
Headers.Builder builder = new Headers.Builder();
//遍歷 字符串數(shù)組
for (String header : headers) {
獲取':'的position下標
int colon = header.indexOf(':');
//如果不存在字符':',或者在最前面辣恋,或者在最后面 這是不符合header標準亮垫,所以拋異常
if (colon == -1 || colon == 0 || colon == header.length() - 1) {
throw methodError(
"@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
}
//按照':'分類,在':'前面為name伟骨,在':'后面為value
String headerName = header.substring(0, colon);
String headerValue = header.substring(colon + 1).trim();
//按照忽略大小寫的方法來判斷是否是"Content-Type"饮潦。
if ("Content-Type".equalsIgnoreCase(headerName)) {
如果是"Content-Type",則來獲取類型
MediaType type = MediaType.parse(headerValue);
//類型不能為null
if (type == null) {
throw methodError("Malformed content type: %s", headerValue);
}
把類型賦給contentType
contentType = type;
} else {
//如果不是"Content-Type"携狭,則添加到 Headers.Builder 里面
builder.add(headerName, headerValue);
}
}
//調(diào)用Headers.Builder的build()方法來返回一個okhttp3.Headers對象
return builder.build();
}
- 1继蜡、new 一個Headers.Builder()對象
- 2、for each遍歷headersString數(shù)組
- 3逛腿、以':'為分界線前面為key稀并,后面是value
- 4、如果key是"Content-Type"单默,則獲取響應的類型
- 5碘举、把key和value作為一對,添加到builder中
- 6搁廓、調(diào)用builder的build()來獲取一個okhttp3.Headers對象引颈,并把它返回。
由于受到簡書的篇幅限制境蜕,剩下的內(nèi)容我們將在下一篇文章中解讀