【Android】Retrofit源码学习
使用Retrofit的流程
通过Builder创建Retrofit对象:
Retrofit retrofit = new Retrofit.Builder().baseUrl("").addConverterFactory().build();
用Java注解描述API
public interface MyApi {
@GET("/api")
Call<Data> getData();
}
通过retrofit创建api对象,并建立Call对象
MyApi api = retrofit.create(MyApi.class);
Call<Data> call = api.getData();
通过Call对象获取数据,
enqueue()方法发送异步请求,同步方式则使用execute()方法call.enqueue(new Callback<Data>() {
@Override
public void onResponse(Response<ZhuanLanAuthor> author) {
System.out.println("name: " + author.getName());
}
@Override
public void onFailure(Throwable t) {
}
});
原理解析
Retrofit所做的事情:将Java接口翻译成HTTP请求,然后用OkHttp去发送请求。
Retrofit使用动态代理实现了这件事
动态代理
动态代理可以在不实现相同接口的proxy的情况下,对相关方法进行代理。
Java可以通过Proxy类实现代理模式,而其中的newProxyInstance() 方法可以实现动态代理。通过实现InvocationHandler接口来定义代理动作。
Proxy.newProxyInstance(ClassLoader, Class<?>[] interfaces,InvocationHandler)
InvocationHandler的接口定义如下:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
参数含义:
- proxy:代理对象
- method: 代理方法
- args: 方法的参数
实现invoke()方法来进行代理:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// do something
method.invoke(target, args);
// do something
}
这样便能够成功对target方法进行代理,动态代理生成的代理类的名字为包名+$Proxy+id序号
请求流程分析
回到使用方法,一开始要使用create()生成API的对象
MyApi api = retrofit.create(MyApi.class);
这里看下create()的源码:
public <T> T create(final Class<T> service) {
validateServiceInterface(service); // 判断是否为接口
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// 如果是Object的方法则直接调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 兼容Java8,Android平台不会调用, 确认平台的方法是通过反射机制判断类的加载信息
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 主要方法,返回ServiceMethod对象
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
create使用了动态代理的方法,返回了Proxy.newProxyInstance()动态代理对象
所以api对象为动态代理对象,不是真正的实现接口产生的对象。当api对象调用getData()方法时会被动态代理拦截,然后调用InvocationHandler对象中的invoke()方法。
然后Retrofit通过反射获取到getData()方法的注解信息,配合invoke()的args参数,创建一个ServiceMethod对象
ServiceMethod传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api的域名、path、http请求方法、请求头、body等等。最后返回一个Call对象。
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method); // 如果有缓存则直接用
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method); // 线程安全,锁住后查看其他线程是否有加载
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method); // 解析注解
serviceMethodCache.put(method, result); // 放入Cache中
}
}
return result;
}
跟进ServiceMethod.parseAnnotation():
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 解析注解为HTTP请求的相关信息
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) { //API接口方法返回值不能为void
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
这里构建了一个RequestFactory对象,解析了接口中关于Http协议的相关信息,具体解析方法就是拿到Method的Annotation后instansof比较确定
RequestFactory(Builder builder) {
method = builder.method;
baseUrl = builder.retrofit.baseUrl;
httpMethod = builder.httpMethod;
relativeUrl = builder.relativeUrl;
headers = builder.headers;
contentType = builder.contentType;
hasBody = builder.hasBody;
isFormEncoded = builder.isFormEncoded;
isMultipart = builder.isMultipart;
parameterHandlers = builder.parameterHandlers;
isKotlinSuspendFunction = builder.isKotlinSuspendFunction;
}
解析完后使用HttpServiceMethod.parseAnnotations()最后生成HttpServiceMethod对象
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; // kotlin支持,先忽略
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations(); // 方法的注解 @GET @POST @DELETE等
Type adapterType;
// ...这里有一段关于Kotlin支持的代码,adapterType
adapterType = method.getGenericReturnType(); // 接口方法的返回类型,一般为Call<T>
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(method, "'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// HEAD请求没有Response Body
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
// 设置Response的解析,可以是json解析
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory; // 这里若是没有自定则默认为OkHttpClient
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); // 不使用Kotlin就关注这里就行了
}
// ...关于Kotlin的return
}
最后返回了HttpServiceMethod的继承类CallAdapted,其中存放着RequestFactory、Converter、CallFactory
然后我们返回来看这段代码
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
这里调用的invoke方法来为HttpServiceMethod中的invoke方法:
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
// CallAdapted中
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
这里的OKHttpCall为Okhttp3.Call的封装类,并实现了Call的相关方法enqueue、execute。
这里最后使用的adapt方法调用了Retrofit对象中的callAdapter.adapt()来对Call对象进行了适配。
若是开始初始化Retrofit对象时没有设置CallAdapter,则回默认使用Call<T>,api接口定义时方法的返回类型只能是Call<T>
所以便能解释如下代码:
Call<MyData> call = api.getData();
api对象为一个动态代理对象,当执行getData()时进入动态代理函数,在InvocationHandler的invoke函数最后调用了HttpServiceMethod.invoke(args),返回了一个Call<T>对象。
响应流程分析
Retrofit使用中最后调用自定义的API接口方法返回了Call<T>对象,这个对象实际上是Retrofit自己封装的OkHttpCall对象,随后我们使用enqueue方法发出异步请求
call.enqueue(new CallBack<MyData>() {
@Override
public void onResponse(Call<MyData> call, Response<MyData> response) {
//... on response
}
@Override
public void onFailure(Call<MyData> call, Throwable t) {
//... on response
}
})
跟进OkHttpCall.enqueue的源码:
@Override public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null"); // callback不能为null
okhttp3.Call call; // okhttp3的Call对象
Throwable failure;
synchronized (this) { // 线程安全
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall; // rawCall为OkHttpCall保存的Okttp3的Call对象
failure = creationFailure;
if (call == null && failure == null) {
try {
// createRawCall中使用callFactory.newCall(requestFactory.create(args))
// 实际上就是OkHttpClient.newCall(OkHttp3.Request)
// 返回了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();
}
// 使用okhttp3.Call的enqueue方法
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
// 这里使用了Converter来解析Response
// 将Okhttp3.Response对象解析成Retrofit封装的Response对象
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
// 调用传进来的回调
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace();
}
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
// 请求失败则进入callback的OnFailure方法
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace();
}
}
});
}
其中parseResponse()方法:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// 将Response Body 和 ResponseHeader 分开
// 之后再对Body进行处理
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code(); // HTTP 状态码
// 响应不成功
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
// 响应无内容,填入null
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
// 保存source的Response Body,在解析失败时可以使用
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// 使用responseConverter来解析Body
T body = responseConverter.convert(catchingBody);
// 将解析好的Body装入Retrofit的Response对象返回
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
主要的parse过程便是,将Okhttp.Response对象的Body和Header拆开,若请求成功且Body有内容则将Body交给responseConverter取解析成响应对象,装入Retrofit的Response对象中返回。
总结
- Retrofit的特色:通过使用注解定义API接口的方式声明API,通过注解的解析,将解析得到的信息封装在RequestFactory中,在使用时调用
create()方法生成Okhttp的Request对象。 - 通过动态代理的方式,代理用户定义的API接口方法,使其生成封装的OkHttpCall对象
- 封装okhttp.Call为OkHttpCall,使其能够使用CallAdapter(可以使返回的Call对象适配为其他的对象,如RxJava(没用过)中的对象)和ResponseConverter(支持Gson等解析)
- 目前只读到这里,还有一些机制没读完
参考文章
https://www.jianshu.com/p/c1a3a881a144
https://segmentfault.com/a/1190000006767113
https://yq.aliyun.com/articles/658544
【Android】Retrofit源码学习的更多相关文章
- Android多线程源码学习笔记一:handler、looper、message、messageQueue
最近在学习Android多线程相关知识的源码,现在把自己的笔记整理一下,写出来加深印象. Android多线程通讯的核心是handler.looper.message.messageQueue,这篇文 ...
- [旧][Android] Retrofit 源码分析之 ServiceMethod 对象
备注 原发表于2016.05.03,资料已过时,仅作备份,谨慎参考 前言 大家好,我又来学习 Retrofit 了,可能这是最后一篇关于 Retrofit 框架的文章了.我发现源码分析这回事,当时看明 ...
- [旧][Android] Retrofit 源码分析之执行流程
备注 原发表于2016.04.23,资料已过时,仅作备份,谨慎参考 前言 由于是第一次自己翻看源代码进行学习,加上基础不好,在看源代码的过程中简直痛苦不堪,但同时也暴露出了自己的许多问题.我觉得学习源 ...
- Android系统源码学习步骤
Android系统是基于Linux内核来开发的,在分析它在运行时库层的源代码时,我们会经常碰到诸如管道(pipe).套接字(socket)和虚拟文件系统(VFS)等知识. 此外,Android系统还在 ...
- [旧][Android] Retrofit 源码分析之 Retrofit 对象
备注 原发表于2016.04.27,资料已过时,仅作备份,谨慎参考 前言 在上一周学习了一下 Retrofit 的执行流程.接下来的文章要更为深入地学习 Retrofit 的各个类,这次我们先学习一下 ...
- [置顶]
【Android实战】----从Retrofit源码分析到Java网络编程以及HTTP权威指南想到的
一.简介 接上一篇[Android实战]----基于Retrofit实现多图片/文件.图文上传中曾说非常想搞明白为什么Retrofit那么屌.最近也看了一些其源码分析的文章以及亲自查看了源码,发现其对 ...
- Android源码学习之装饰模式应用
首先得了解最基础的装饰器模式 参考 设计模式之八 --- 装饰模式(Decorator) 参考链接:http://blog.csdn.net/cjjky/article/details/7478788 ...
- Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习
package cc.aa; import android.os.Environment; import android.view.MotionEvent; import android.view.V ...
- [Android FrameWork 6.0源码学习] View的重绘过程之WindowManager的addView方法
博客首页:http://www.cnblogs.com/kezhuang/p/关于Activity的contentView的构建过程,我在我的博客中已经分析过了,不了解的可以去看一下<[Andr ...
随机推荐
- 密码子演化假说|凝固事件假说|立体化学假说|共进化假说|代谢途径相关性假说|四重兼并|假四重兼并|最小损伤原则|AU-rich|GC-rich|逐步进化假说|分子机制进化假说
生命组学 将密码子表重排后发现,嘌呤嘧啶含量不同,密码子的氨基酸种类由第一二位决定,同时第三位变化大却没有蛋白质层面上实质性的改变,这说明第三位氨基酸是用于维持氨基酸组成不发生变化同时保证蛋白质稳定性 ...
- JavaScript秒转换成天-小时-分钟-秒
根据时间秒转换成天-小时-分钟-秒 // 秒转换成day.hour.minutes.seconds formatSecond(second: number) { const days = Math.f ...
- 用Hutton32玩转数字电路(一):逻辑门
最近看到有人用Minecraft里面的红石电路制作出了计算器,还有一篇神文:<基于Minecraft实现的计算机工程>,视频在此,好像还能算浮点数.三角函数.我对红石不是太了解,那能不能用 ...
- 使用junit测试springMVC项目提示ServletContext找不到定义错误
原文链接:https://blog.csdn.net/liu_gan/article/details/78400627 @RunWith(SpringJUnit4ClassRunner.class) ...
- vm文件的优点
vm文件的优点 相较于内容写在jsp 文件: 1.在网页上上浏览和下载的内容用的是同一套,也就是说只需要维护一套内容,页面上看到的和下载得到的是一致的. 2.版本控制较为简便, 实现了页面内容和jsp ...
- CSAPC08台湾邀请赛_T1_skyline
题目链接:CSAPC08台湾邀请赛_T1_skyline 题目描述 一座山的山稜线由许多片段的45度斜坡构成,每一个片段不是上坡就是下坡. / / * / / * / // / // / 在我 ...
- container/injection简介以及发展历史
一:什么是Container?Container的作用? 容器是一个标准的软件单元,它将代码及其所有依赖关系打包,以便应用程序从一个计算环境快速可靠地运行到另一个计算环境.container的主要作用 ...
- HAProxy此例简单介绍基于docker的HAProxy反代
HAProxy拓展连接 此例简单介绍基于Docker的HAProxy反代 反代: 1.获取haproxy镜像 docker pull haproxy 2.写配置文件haproxy.cfg 1 glo ...
- Android API Levels
Android API Levels(转) 在本文中 API的级别是什么? 在Android中使用API级别 开发者需要考虑的内容 应用程序的向前兼容性 应用程序的向后兼容性 平台版本和API级别的选 ...
- Ta说:2016微软亚洲研究院第二届博士生论坛
"聚合多元人才创造无尽可能,让每一位优秀博士生得到发声成长机会"可以说是这次微软亚洲研究院博士生论坛最好的归纳了.自去年首次举办以来,这项旨在助力青年研究者成长的项目迅速得到了 ...