Retrofit源码分析基于2.4.0。
关于Retrofit的基本使用可以参照 Retrofit官网。
Retrofit是Java及Android平台上类型安全的Http客户端。这是官方给出关于Retrofit的简介。
这里需要明确:Retrofit只是一个Http网络请求封装库,该库的主要工作是对网络请求进行封装,最终实现网络请求的操作是由Okhttp完成的。
基本使用
使用Retrofit发起一次网络请求,只需要以下流程即可:
- 定义一个网络请求接口。
- 创建Retrofit实例
- 通过Retrofit创建网络请求接口的动态代理类
- 通过动态代理类获取Call对象,用于网络请求
- 通过Call对象实现网络请求(同步/异步)
代码演示:
1、定义一个网络请求接口实现用户登录功能
public interface ILoginService {
@POST("user/login")
Call Login(@Body User user);
}
2、通过Retrofit完成网络请求
// 2、创建一个Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:8080/testRetrofit/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
// 3、通过Retrofit创建Retrofit的代理实例
ILoginService loginService = retrofit.create(ILoginService.class);
User user = new User();
user.setUsername("jack");
user.setPassword("123456");
// 4、获取实现网络请求的Call对象
Call call = loginService.login(user);
// 5、实现异步网络请求
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
// 获取用户信息
User body = response.body();
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
// 5、同步请求获取响应
// Response execute = call.execute();
// User body = execute.body();
源码分析
针对上文中提到的Retrofit的使用流程,这里将对这些流程的源码逐一分析。
创建Retrofit实例
Retrofit实例的创建主要是通过内部类Retrofit.Buidler实现的。很明显,这里使用了建造者模式。
这里看下Builder的构造方法:
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
Builder(Retrofit retrofit) {
platform = Platform.get();
callFactory = retrofit.callFactory;
baseUrl = retrofit.baseUrl;
converterFactories.addAll(retrofit.converterFactories);
// Remove the default BuiltInConverters instance added by build().
converterFactories.remove(0);
callAdapterFactories.addAll(retrofit.callAdapterFactories);
// Remove the default, platform-aware call adapter added by build().
callAdapterFactories.remove(callAdapterFactories.size() - 1);
callbackExecutor = retrofit.callbackExecutor;
validateEagerly = retrofit.validateEagerly;
}
从源码可知,Retrofit有三个构造方法,但是两个具有包级访问权限,所以外部能使用的就只有无参数构造方法。
这种构造方法的设计和Okhttp是相同的。
在上面的无参构造方法中,内部通过Platform.get()方法获取一个Platform实例以完成Retrofit的创建。
Platform将在下章节中通CallAdapter一同讲解,这里只需要知道在Android平台中该Platform的作用就是提供一个CallAdapter工厂用于创建CallAdapter即可。
接着看下Builder.build()方法,build()方法主要是对Retrofit中一些属性做初始化操作。
Retrofit声明的属性
private final Map> serviceMethodCache = new ConcurrentHashMap<>();
final okhttp3.Call.Factory callFactory;
final HttpUrl baseUrl;
final List converterFactories;
final List callAdapterFactories;
final @Nullable Executor callbackExecutor;
final boolean validateEagerly;
build()方法:
public Retrofit build() {
// 设置baseUrl,必填
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 设置callFactory,用于创建Call实例,这里默认使用的是OkHttpClient
// 所以通过这里可以看出
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 设置异步回调请求执行器,没有设置就使用默认的,在Platform.Android下默认返回主线程(UI线程)执行器
// 主要在异步请求中会使用到
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// 创建CallAdapter集合,用于存放所有的callAdapter网络请求适配器
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// 创建数据解析器。gson、jaskson等
List converterFactories =
new ArrayList<>(1 + this.converterFactories.size());
// 首先会添加一个默认的BuiltInConverters
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
// 创建Retrofit实例
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
其实,build()方法中主要就是完成了Retrofit的一些配置,这个从最后Retrofit的构造方法中可以看出。
设置baseUrl
在Retrofit实例的创建中,baseUrl为必填属性,这里我们看下Builder.baseUrl()的实现。
public Builder baseUrl(String baseUrl) {
// 非空判断
checkNotNull(baseUrl, "baseUrl == null");
// 根据传入的url生成一个HttpUrl对象,这个类主要是对http的url做一些封装
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
// 下面整个逻辑判断传入的url是否是以"/"结尾
List pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
在上面baseUrl()方法中可知,url必须要以“/”结尾,这个也是在使用Retrofit中必须要注意的一点。
创建网络接口代理类
在Retrofit.create()中主要使用到了Java动态代理,如果对动态代理的实现机制不清楚的话,请自行学习,这里就不对动态代理做深入分析了。
在完成了Retrofit实例的创建后,就要通过Retrofit.create()方法创建一个网络请求代理类,这个类根据定义的网络请求接口生成。
ILoginService loginService = retrofit.create(ILoginService.class);
这里看下Retrofit.create()方法的具体实现:
public T create(final Class service) {
// 1、首先判断类service是不是一个接口
Utils.validateServiceInterface(service);
// 2、validateEagerly默认是false
if (validateEagerly) {
// 3、提前加载method,这个方法的内部会调用loadServiceMethod()方法,所以这个将会在下面的动态代理中一并讲解。
eagerlyValidateMethods(service);
}
// 4、创建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, @Nullable Object[] args)
throws Throwable {
// 5、如果方法是来自对象的方法,则正常调用。像equal()、hashcode()等,在网络请求中这种情况一般不会发生。
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 6、在Android.Platform中isDefaultMethod()方法默认返回false,这里就忽略该方法。
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 7,这是动态代理方法中的核心,也是Retrofi他中的核心代码,这里将创建并最终返回网络请求调用的Call实例
ServiceMethod
这里,我们主要关注动态代理类的生成过程,即使用Proxy.newProxyInstance()创建实例的过程。
这里通过Proxy.newProxyInstance()对动态代理做个简单介绍:
通过该方法可以为指定接口(可以是多个接口)生成一个代理类的实例,每当调用接口的相关方法时,最终都会调用InvocationHandler类的invoke方法,而invoke方法的返回值就是接口方法的返回值。
因此,在上面的动态代理invoke()方法中,最终会返回一个调用网络请求的Call对象,该对象就是在网络接口方法中指定的返回值。
获取网络请求Call实例
在上文中提到过,网络请求Call实例是在InvocationHandler的invoke中创建并返回的,也就是上文中的三行核心代码,这里把这三行代码拿出来单独分析。
// 1、获取ServiceMethod实例,
ServiceMethod serviceMethod =
(ServiceMethod) loadServiceMethod(method);
// 2、获取OkhttpCall实例
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 3、最终返回调用网络请求的实例
return serviceMethod.adapt(okHttpCall);
创建ServiceMethod
首先,loadServiceMethod()方法会获取一个ServiceMethod对象,这个loadServiceMethod()方法主要是处理网络接口的注解,最终获取http请求相关的所有参数(url、请求头、请求响应解析器等)。
代码如下:
ServiceMethod, ?> loadServiceMethod(Method method) {
// serviceMethodCache是一个LinkedHashMap,用于缓存ServiceMethod
// LinkedHashMap内部使用了LRU算法
ServiceMethod, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 如果缓存中不存在,那么就根据method创建一个ServiceMethod并放入到缓存中,以便下次使用。
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
ServiceMethod类
在上文loadServiceMethod()方法中最终是通过new ServiceMethod.Builder<>(this, method).build()创建一个ServiceMethod实例的。
这里我们直接看ServiceMethod.Builder的构造方法和build()方法,如下:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// 1、获取方法的注解
this.methodAnnotations = method.getAnnotations();
// 2、获取方法中参数的类型,这是一个Type[]
this.parameterTypes = method.getGenericParameterTypes();
// 3、获取方法中每个参数的注解,这是一个二维数组Annotation[][]
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
public ServiceMethod build() {
// 1、创建CallAdapter网络适配器,通过工厂方法创建
callAdapter = createCallAdapter();
// 2、获取网络响应体类型
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
// 这里可以看出响应体需要是ResponseBody
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
// 3、创建响应解析器,通过工厂方法创建,创建过程和callAdapter方法类似
responseConverter = createResponseConverter();
// 4、解析网络接口方法的注解,所有作用于method的注解都在这里解析
// 例如(GET、POST、HTTP、Multipart、FormUrlEncoded、Headers等)
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
// 必须设置Http请求类型
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
// 5、获取网络接口方法的参数的数量
int parameterCount = parameterAnnotationsArray.length;
// 6、创建一个数组,用于存储每个方法参数的注解等信息
parameterHandlers = new ParameterHandler>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
// 7、获取第p个参数的类型
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
// 8、获取第p个该数上所有的注解
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
// 9、根据参数及参数的注解生成一个ParameterHandler对象
// ParameterHandlers类中定义了不同注解的类,用于封装参数。
// 最终通过ParameterHandler.apply()方法将参数设置到RequestBuilder中
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
// 10、创建ServiceMethod对象
return new ServiceMethod<>(this);
}
这上面代码中对build()方法中的具体功能都做了说明,这里选取几个关键代码深入分析一下。
创建CallAdapter
首先,在build()方法中会通过createCallAdapter()方法获取CallAdapter网络适配器,这个是通过工厂方法生成的,如下:
private CallAdapter createCallAdapter() {
// 1、获取接口方法的返回值类型
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
// 2、获取该方法中所有的注解
Annotation[] annotations = method.getAnnotations();
try {
// 3、获取CallAdapter对象,这个最终会通过工厂类Factory.get()方法实现,具体实现在子类中如RxJava2CallAdapterFactory
return (CallAdapter) 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);
}
}
CallAdapter机制将在下一章节中单独讲解,这里先简单介绍一下。
同样的createResponseConverter()方法创建响应转换器的过程也和createCallAdapter()过程是类似的。
初始化ParameterHandler数组
在build()方法中会获取网络接口参数及对应的注解类型,并将每个参数都根据注解类型封装成不同的ParameterHandler类,最终ParameterHandler数组主要用于RequestBuilder的创建。
该过程比较长,这里就不贴上代码了,可以自行通过build()方法第9个注释处查看具体代码。
创建OkHttpCall
OkHttpCall:就是对Okhttp中的Call做了一层封装,最终在OkHttpCall中会通过Okhttp.Call完成网络请求的操作
完成了ServiceMethod对象的创建后,在InvovationHandler.invoke()方法中就要通过ServiceMethdo对象和网络接口参数,创建一个OkHttpCall对象,这里看下OkHttpCall的构造方法:
OkHttpCall(ServiceMethod serviceMethod, @Nullable Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
这个构造方法很简单,就是完成了两个属性的赋值操作。
OkHttpCall是实现网络请求的具体实现类,这个将在下文中具体分析。
在获取到ServiceMethod和OkHttpCall对象后,在Retrofit中将通过ServiceMethod的adapt(OkHttpCall)方法获取网络接口的动态代理对象。
serviceMethod.adapt(okHttpCall);
ServiceMethod.adapt()方法的内部很简单,通过具体的CallAdapter网络适配器获取Call对象,如下:
T adapt(Call call) {
return callAdapter.adapt(call);
}
这样在整个Retrofit.create()方法中就通过动态代理完成了Call对象的获取,这样就可以使用OkHttpCall完成网络请求了。
CallAdapter机制将在下一章节中单独讲解,这里先简单介绍一下。
OkHttpCall实现同步异步请求
在通过网络接口获取了Call对象后,就可以开始完成网络请求了。
这个过程主要是通过Call的具体实现类OkHttpCall实现的,这里对实OkHttpCall的同步请求和异步请求的实现做具体分析。
同步请求方法execute
在获取了网络接口定义的Call对象后,要想实现同步请求只需要调用Call.execute()方法即可,这里根据上一章节中实例演示同步请求代码:
// 5、同步请求获取响应
Response execute = call.execute();
//
User body = execute.body();
OkHttpCall中的同步方法execute()的逻辑非常请求,主要包括:
- 创建一个okhttp3.Call实例
- 调动okhttp3.Call的同步方法execute获取响应
- 解析响应结果并返回
execute()方法如下:
@Override
public Response execute() throws IOException {
// 1、定义一个okhttp的Call
// 看到这里就很清晰了,Retrofit是通过okhttp完成网络请求的
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
// 2、创建okhttp中的call对象
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
// 3、调用同步方法,并完成响应结果的解析
return parseResponse(call.execute());
}
创建okhttp3的Call对象
在上面execute()方法中,okhttp3.Call对象主要是通过createRawCall()方法创建的,这里看下该方法:
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = serviceMethod.toCall(args);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
在该方法中,主要通过serviceMethod.toCall()方法完成okhttp3.Call对象的创建。
接下来,我们看下toCall()方法:
okhttp3.Call toCall(@Nullable Object... args) throws IOException {
// 1、创建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[] handlers = (ParameterHandler[]) 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 + ")");
}
// 2、网络请求接口参数封装到RequestBuilder对象中,这样可以获取整个网络请求完整的参数
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
// 3、通过RequestBuilder.build()方法创建okhttp3所需要的Request对象
// 4、最终通过CallFactory.newCall()创建okhttp3网络请求的Call对象(这个CallFactory对象就是在Retrofit创建时创建的OkHttpClient对象)
return callFactory.newCall(requestBuilder.build());
}
toCall方法的功能逻辑也很清晰:
- 封装所有网络请求参数到RequestBuilder
- RequestBuilder.build()方法将所有参数都解析成Okhttp3的网络参数的格式。并最终生成一个okhttp3.Request对象。
- 通过CallFactory也就是OkHttpClient对象创建一个Call实例。
这里需要注意的就是Retrofit中的RequestBuilder类,该类中实现了Retrofit到okhttp3的转换。
解析okhttp请求结果
当Retrofit通过okhttp3完成了同步请求后,在获取请求结果后,将会对该结果进行解析,最终将okhttp的结果转化成Retrofit的请求结果。
这里我们看下OkHttpCall.parseResponse()方法:
Response parseResponse(okhttp3.Response rawResponse) throws IOException {
// 1、获取okhttp中的响应体
ResponseBody rawBody = rawResponse.body();
// 这里会移除响应体,在处理一些错误时会使用Retrofit中定义的响应体,例如Response.error()方法
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
// 2、获取响应码
int code = rawResponse.code();
// 异常结果处理 start
if (code < 200 || code >= 300) {
try {
// 返回错误响应
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
// 异常结果处理 end
// 3、对响应结果进行处理
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
// 4、通过响应解析器将结果转化成对应的响应结果类
T body = serviceMethod.toResponse(catchingBody);
// 5、获取具体Response类(body:网络请求接口中定义的结果类;rawResponse:okhttp3中解析的响应体)
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
在parseResponse()方法中,主要通过serviceMethod.toResponse()方法将okhttp的响应体内容解析成网络接口中指定的类型,在这个方法中主要使用了响应解析器,如下:
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
最后通过Response.success(body, rawResponse)方法创建一个Response对象,该对象中封装了通过okhttp3获取的网络请求。
public static Response success(@Nullable T body, okhttp3.Response rawResponse) {
checkNotNull(rawResponse, "rawResponse == null");
if (!rawResponse.isSuccessful()) {
throw new IllegalArgumentException("rawResponse must be successful response");
}
return new Response<>(rawResponse, body, null);
}
这样整个同步请求就完成了。
异步请求方法enqueue
通过Call对象实现异步请求,只需要调用OkHttpCall.enqueue()方法即可:
// 实现异步网络请求
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
// 获取用户信息
User body = response.body();
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
异步网络请求enqueue()方法内部的逻辑也非常清晰,基本和上文中同步请求的步骤一致,主要包括:
- 创建一个okhttp3.Call实例
- 调动okhttp3.Call的同步方法enqueue获取响应
- 解析响应结果
- 最终调用回调接口传递网络请求结果
@Override
public void enqueue(final Callback callback) {
checkNotNull(callback, "callback == null");
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 {
// 1、创建okhttp3.Call对象
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
// 2、调用okhttp3.Call的异步方法enqueue()
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response response;
try {
// 3、解析网络响应
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
try {
// 4、调用回调方法
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
异步方法的主要逻辑基本在同步方法中讲过了,这里就不在介绍了。
总结
这样,整个Retrofit完成网络请求的流程的源码就都分析完了。
在该源码分析中,CallAdapter获取具体网络适配器的实现原理将会在下一章节中具体讲解。