Retrofit源码分析(一)
1.基本用法
- 创建接口
public interface GitHubService {
@GET("users/{user}/repos")
Observable<List<Repo>> listRepos(@Path("user") String user);
}
- 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
- 生成接口实现类
GitHubService service = retrofit.create(GitHubService.class);
- 调用接口实现类的请求方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");
- 调用Call对象的异步执行方法
repos.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Response<List<Repo>> response) {
// do something
}
@Override
public void onFailure(Throwable t) {
// do something
}
});
2.主线剧情的源码
- 构造接口
通过注解的方式来构造接口,以GET方法为例:
@Documented
@Target(METHOD)//注解用于方法
@Retention(RUNTIME)//该注解运行时可用(TIJ P622)
public @interface GET {
String value() default "";
}
- 创建Retrofit实例
此处使用了建造者模式,仅以baseurl为例,其余代码省略
public static final class Builder {
private Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
//...
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
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");
List<String> pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
//...
}
可以看见有两个List,ConverterFactories和adapterFactories。
在配置它们的时候,使用了保护性拷贝
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
这是后文的重要角色,Retrofit为了解耦合,使用了CallAdapter用来做底层网络请求适配,以及Converter做数据格式的适配。
Converter分为requestConverter、responseConverter和StringConverter三种,可以对请求和响应数据进行包装转换,在定义Retrofit对象时可以自己制定。
在请求时Retrofit使用的是泛型,当把Http Response装换成Java对象时,利用Converter将其转化为请求原本需要的数据类型,完成不用数据格式的适配。
关于Converter我会后续再专门写一篇,也可以阅读以下下面的博客
- 生成接口实现类
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(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, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
这里就使用了动态代理
- 动态代理
动态代理机制是Java的一个高级特性, 其主要功能就是可以为委托类对象生成代理类, 代理类可以将所有的方法调用分派到委托对象上反射执行. 动态代理的相关知识可参考 相关的Java书籍.
这里传入newProxyInstance()有三个参数:
- 接口的classLoader.
- 只包含接口的class数组.
- 自定义的InvocationHandler()对象, 该对象实现了invoke() 函数, 通常在该函数中实现对委托类函数的访问.
分析一下上面的代码:
首先做了两个验证,验证接口和标识。
如果我们调用的是来自 Object 类或者平台默认的方法,则会交给方法执行或者平台执行,但从代码上看 isDefaultMethod(method) 直接返回的是 false,可能是为了方便开发者扩展设置各个平台的不同方法调用。
经过两个判断后,会将我们的方法转换成一个 ServiceMethod对象,这是 Retrofit 中最核心的一个类,通过 ServiceMethod 中存储的请求信息向网络方向可以根据 Call 构建出真实的请求实体进行网络请求。
看一下loadServiceMethod方法的实现:
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
下面是ServiceBuilder的一部分代码:
static final class Builder<T> {
//...
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
//...
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
//...
}
//...
}
可见,我们通过解析注解的方式来生成ServiceMethod对象,但是解析注解不是效率很低么?
对,这里为了解决效率问题,使用了ServiceMethodCache,利用缓存获取已经解析过注解的ServiceMethod。
最后看看最核心的三行代码:
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
生成接下来创建了一个 OkHttpCall。
并使用 serviceMethod.CallAdapter 对 OkHttpCall 进行了转化。
这里看起来有点绕,用ServiceMethod生成了OkHttpCall后,还要用serviceMethod去adapt它,形成了一个闭环,其实可以将这里理解为一种跟踪。
而callAdapter是用来做类型转换的,将call对象转换成合适的网络请求方式,默认是OKHttp,这里的设计也是为了方便与RxJava相结合,将OKHttpCall进行一次封装,编程Retrofit的Call,如果你想的话,也可以自己写一个HTTPURLConnectionCall,然后再封装成Retrofit的call。
在 Android 平台,我们使用了 ExecutorCallAdapterFactory 封装网络请求为一个 Call 并将请求结果转发到主线程
Retrofit.create()实际上对 REST 服务接口做了一个动态代理,并返回一个实例,用来完成实际的 http 请求。
- 获取Call对象
直接按需调用我们声明出来的服务接口实例的自定义方法即可,此时Retrofit会为通过动态代理你生成一个需要的HTTP请求——Call
- 执行请求
获得Call一般式OKHttpCall
OKHttpCall是用来操作请求的,包括同步执行(execute),异步执行(enqueue),取消请求等功能。Retrofit最新版本默认使用OKHttp去做网络请求。
最后会回调我们定义好的CallBack接口中的\onResponse或者onFailture
到此为止,主线任务上的源码逻辑就理清了。
Retrofit源码1: 为什么写一个interface就可以实现http请求
Retrofit源码分析(一)的更多相关文章
- [置顶]
【Android实战】----从Retrofit源码分析到Java网络编程以及HTTP权威指南想到的
一.简介 接上一篇[Android实战]----基于Retrofit实现多图片/文件.图文上传中曾说非常想搞明白为什么Retrofit那么屌.最近也看了一些其源码分析的文章以及亲自查看了源码,发现其对 ...
- [旧][Android] Retrofit 源码分析之 ServiceMethod 对象
备注 原发表于2016.05.03,资料已过时,仅作备份,谨慎参考 前言 大家好,我又来学习 Retrofit 了,可能这是最后一篇关于 Retrofit 框架的文章了.我发现源码分析这回事,当时看明 ...
- [旧][Android] Retrofit 源码分析之执行流程
备注 原发表于2016.04.23,资料已过时,仅作备份,谨慎参考 前言 由于是第一次自己翻看源代码进行学习,加上基础不好,在看源代码的过程中简直痛苦不堪,但同时也暴露出了自己的许多问题.我觉得学习源 ...
- [旧][Android] Retrofit 源码分析之 Retrofit 对象
备注 原发表于2016.04.27,资料已过时,仅作备份,谨慎参考 前言 在上一周学习了一下 Retrofit 的执行流程.接下来的文章要更为深入地学习 Retrofit 的各个类,这次我们先学习一下 ...
- 源码分析Retrofit请求流程
Retrofit 是 square 公司的另一款广泛流行的网络请求框架.前面的一篇文章<源码分析OKHttp执行过程>已经对 OkHttp 网络请求框架有一个大概的了解.今天同样地对 Re ...
- Android网络框架源码分析一---Volley
转载自 http://www.jianshu.com/p/9e17727f31a1?utm_campaign=maleskine&utm_content=note&utm_medium ...
- Retrofit源码设计模式解析(上)
Retrofit通过注解的方法标记HTTP请求参数,支持常用HTTP方法,统一返回值解析,支持异步/同步的请求方式,将HTTP请求对象化,参数化.真正执行网络访问的是Okhttp,Okhttp支持HT ...
- Retrofit2源码分析(一)
本文将顺着构建请求对象→构建请求接口→发起同步/异步请求的流程,分析retrofit2是如何实现的. 组成部分 Retrofit2源码主要分为以下几个部分: retrofit retrofit-ada ...
- springcloud 入门 5 (feign源码分析)
feign:(推荐使用) Feign是受到Retrofit,JAXRS-2.0和WebSocket的影响,它是一个jav的到http客户端绑定的开源项目. Feign的主要目标是将Java Http ...
随机推荐
- 如何使用THashedStringList
1.添加 uses system.IniFiles 2.实例代码: unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System ...
- 移动端自动化环境搭建-stuptools和pip的安装
安装stuptools和pip A.安装依赖 setuptools 和 pip 并非必须安装的两个包,但安装之后,后续再安装 Python 的库将变得非常简单. B.安装过程
- Mac Aria2 使用Privoxy将socks代理转化为http代理
安装Privoxy 打开终端安装privoxy来实现这里我是通过brew来进行的安装 brew install privoxy 看到这行已经安装成功 ==> Caveats To have la ...
- vim的树形菜单NERDTREE的设置
网上比较好的一篇文章:http://coolshell.cn/articles/1679.html http://coolshell.cn/articles/11312.html 1.Vim安装NER ...
- myeclipse中配置maven
1.myeclipse中已默认安装maven,首先在window-preferences-myeclipse-maven下找到maven插件,不同的版本位置可能不同,但都可以在window-prefe ...
- MAC系统生成RSA公钥私钥
进入openssl然后主要就是三条命令: 1.genrsa -out rsa_private_key.pem 1024 这句是生成原始私钥文件 2.pkcs8 -topk8 -inform PEM - ...
- openssl大纲
1.加密和SSL机制:http://www.cnblogs.com/f-ck-need-u/p/6089523.html 2.openssl命令总指挥:http://www.cnblogs.com/f ...
- Winform 获取当前单击的控件名称 和 向窗体添加控件
Winform如何获取当前单击的控件名称,比如有100个Button 和一个button_Click()的按钮事件 ,分别点击不同按钮后显示所点击的按钮名称?private void button_C ...
- {Reship}{原文}{资治通鉴}
this article came from here ================================================= 资治通鉴 (361人评分) 9.0 作者 ...
- avalon全选效果分析讲解
全选功能就是 1.点击全选控制循环元素是否选中.(点击全选,下面的所有元素选中,再次点击 所有元素取消选中.) 2.点击循环元素控制全选.(如果当前元素是未选中状态则全选不选中,如果当前元素是选中状态 ...