承接上篇我们讲了Retrofit获取一个对OkHttpCall的封装对象(Call对象)的过程,调用该对象的请求方法就可以执行同步或异步请求了。上篇为了让大家先从整体流程熟悉Retrofit,有些细节没有具体讲解,本篇对一下几点进行源码分析:
- 怎么对OKHttp进行封装的,即OkHttpCall类。
- 怎么创建OKHttp请求的Request对象的,即怎么解析注解,组织一个完整的Request。
- Converter的创建、调用、实现。
OkHttpCall 源码分析
OkHttpCall是对OkHttp请求的一个封装,当调用OkHttpCall请求方法时,OkHttpCall主要做了三件事情:
- 创建OkHttp的请求对象Call。
- Call调用execute请求方法,完成请求。
- 解析响应数据,返回结果。
我们以同步请求方法execute()来分析源码:
@Override
public Response execute() throws IOException {
okhttp3.Call call;
......//异常处理
call = rawCall;
if (call == null) {
try {
//创建一个OKHttp的Call
call = rawCall = createRawCall();
} catch (IOException | RuntimeExceptione) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
//执行请求,解析Response,返回结果
return parseResponse(call.execute());
}
异步求方法enqueue过程大致相同,不再贴代码。 在这里提醒大家的一点:Retrofit中也定义了一个Call接口,也是用作请求封装的,跟OkHttp的Call接口同名,大家注意区分。至于OkHttp的知识我们后续专门文章跟大家一块学习。
进一步看下createRawCall()方法如何创建Call的。
private okhttp3.Call createRawCall() throws IOException {
// 创建request
Request request = serviceMethod.toRequest(args);
// 创建call,其实就是一个RealCall
okhttp3.Call call = serviceMethod.callFactory.newCall (request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
创建OKHttp的Call需要创建一个Request,然后传给callFactory创建一个Call对象,我们找到callFactory初始化的地方,看下callFactory是什么:
.....
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
.....
callFactory就是OKHttp中的OkHttpClient。Call创建完,剩下的工作就交给OKHttp了。
至此,我们分析了请求封装流程,并且知道调用创建Request对象的地方,接下来看Request对象怎么创建。
创建OKHttp请求的Request对象
Requst的创建主要涉及到类有:
- RequestBuilder:Request构造器,用来构造Request。
- ServiceMethod:解析请求接口方法的注解,包括方法注解信息、参数注解信息。
- ParameterHandler:参数处理器,用来处理参数注解信息和实际传入参数数据,并将处理后的参数数据传递给RequestBuilder,用作构造Request对象。
从ServiceMethod的toRequest(args)方法看起:
Request toRequest(@Nullable Object... args) throws IOException {
//创建RequestBuilder 对象
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,contentType, hasBody, isFormEncoded, isMultipart);
......//异常处理
//遍历参数处理器parameterHandlers来处理每个参数定义和具体参数args[p]
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
- 创建一个RequestBuilder。
- 遍历参数处理器parameterHandlers来处理每个参数定义和具体参数args[p]。
- 最后通过请求构造器RequestBuilder 构造一个Request。
RequestBuilder 初始化传入的一串参数和parameterHandlers都是在ServiceMethod初始化的时候,解析请求接口方法的注解得到的。分为方法注解解析和参数注解解析,下面具体看一下:
注解解析
方法注解解析:
解析方法注解的方法是parseMethodAnnotation(Annotation annotation) :
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
......//各种方法注解的判断和解析,不都贴出
}
对方法注解进行解析,继续看到parseHttpMethodAndPath方法:
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
}
//获得请求类型,是否有body数据
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
......
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
这样就得到httpMethod 、hasBody 、relativeUrl等 构造RequestBuilder 所需要的部分参数数据,其他方法注解解析同理,不再批量贴代码。
参数注解解析:
参数标解析parseParameter方法,同样是ServiceMethod初始化时调用,返回的是第p个参数的处理器parameterHandlers[p],代码如下
{
this.parameterHandlers[p] = parseParameter(p,parameterType,parameterAnnotations);
}
parseParameter方法又调用到parseParameterAnnotation实现参数注解解析,注解太多了,就以常用的Query注解,看下主要代码,其他注解同理:
private ParameterHandler> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
.......
if (annotation instanceof Query) {
//解析注解数据value、encoded
Query query = (Query) annotation;
String name = query.value();
boolean encoded = query.encoded();
Class> rawParameterType = Utils.getRawType(type);
gotQuery = true;
//创建请求转换器
Converter, String> converter = retrofit.stringConverter(type, annotations);
//创建参数处理器的具体实现类
return new ParameterHandler.Query<>(name, converter, encoded);
}
........
Query解析:
- 判断为Query注解,拿到Query的value。
- 创建了对应该Query注解的一个Converter。
- 最后创建并返回了ParameterHandler.Query。
Query是ParameterHandler的实现类,并且是对应Query注解的参数处理类。其他参数注解同理,都有一个ParameterHandler实现类与之对应。
----------------------------------注解解析完成--------------------------------
注解解析讲解完成,回头看toRequest方法中调用ParameterHandler的apply方法,还是ParameterHandler.Query为例:
Override void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) return;
//将请求传入的参数值转换成String
String queryValue = valueConverter.convert(value);
if (queryValue == null) return;
//参数数据添加给RequestBuilder
builder.addQueryParam(name, queryValue, encoded);
}
apply方法,将传入的参数值通过Converter转换成字符串类型,作为get请求url参数的value,name就是get请求url参数的key,然后添加给RequestBuilder 。
继续看到addQueryParam方法:
void addQueryParam(String name, @Nullable String value, boolean encoded) {
......//异常处理
if (encoded) {
urlBuilder.addEncodedQueryParameter(name, value);
} else {
urlBuilder.addQueryParameter(name, value);
}
}
最终参数信息添加到 urlBuilder对象中,其他参数注解同理都是通过ParameterHandler的apply方法处理后添加到RequestBuilder中保存,用于Requst构建。
RequestBuilder所有数据准备完毕以后调用build方法,最终构建Request:
Request build() {
HttpUrl url;
//构造完整的Url
HttpUrl.Builder urlBuilder = this.urlBuilder;
if (urlBuilder != null) {
url = urlBuilder.build();
} else {
url = baseUrl.resolve(relativeUrl);
if (url == null) {
throw new IllegalArgumentException(
"Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
}
}
//构造完整的body
RequestBody body = this.body;
if (body == null) {
if (formBuilder != null) {
body = formBuilder.build();
} else if (multipartBuilder != null) {
body = multipartBuilder.build();
} else if (hasBody) {
body = RequestBody.create(null, new byte[0]);
}
}
MediaType contentType = this.contentType;
if (contentType != null) {
if (body != null) {
body = new ContentTypeOverridingRequestBody(body, contentType);
} else {
requestBuilder.addHeader("Content-Type", contentType.toString());
}
}
//构造完整的Request
return requestBuilder
.url(url)
.method(method, body)
.build();
}
build方法中,先构造完整的url和body,然后构建并返回完整的Request。
Request构造涉及到大量注解解析,繁琐但没有逻辑难度。因为不同注解意义不同,处理是有区别的,但是解析流程是和我们上面讲解的流程一样的。
Converter的创建、调用、实现
Converter按用途分为两类:
- Converter
responseConverter将响应数据转换成响应实体对象。 - Converter
或者 Converter requestConverter,用来将请求实体对象转换成对应的请求数据。
responseConverter的创建、调用、实现
responseConverter的创建:
上篇我们已经讲到,ServiceMethod初始化时候调用了Retrofit中的
public ConverterresponseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}
具体创建上篇中已讲到,是通过converter.Factory创建,不在赘述。
responseConverter的调用:
是在OkHttpCall 中解析响应数据parseResponse方法中调用的
T body = serviceMethod.toResponse(catchingBody);
继续看到toResponse方法,
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
responseConverter的实现:
以GsonResponseBodyConverter为例:
Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
requestConverter的创建、调用、实现
requestConverter的创建:
Query注解解析中,已经提到创建requestConverter。
.......
//创建请求转换器
Converter, String> converter = retrofit.stringConverter(type, annotations);
........
调用到Retrofit的stringConverter方法,具体创建也是通过converter.Factory创建,不再赘述。
requestConverter的调用:
是在ParameterHandler中参数处理apply方法中调用的。参考上面的代码。
requestConverter的实现:
以GsonRequestBodyConverter为例:
@Override public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter, value);
jsonWriter.close();
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
将请求实体对象转换成RequestBody。
注意
- 本文是对上篇源码分析的补充分析, 请先对上篇的Retrofit核心流程熟悉,再看本文。
- 本文按照代码阅读的顺序来分析的源码,从OkHttpCall 讲到的request创建、注解解析、convert使用。实际调用顺序不是这样,比如注解解析是在ServiceMethod创建时完成的,是在request创建之前。
- 本文为了突出主要源码,略掉异常处理的代码,这部分代码大部分是用作检查请求接口的规范性。