[旧][Android] Retrofit 源码分析之 Retrofit 对象
备注
原发表于2016.04.27,资料已过时,仅作备份,谨慎参考
前言
在上一周学习了一下 Retrofit 的执行流程。接下来的文章要更为深入地学习 Retrofit 的各个类,这次我们先学习一下 Retrofit 框架里的 Retrofit 对象,有没有十分的拗口。。

本文主要讲 Retrofit 对象的创建及其 .create 方法。基本包括了这个类的全部内容。
Retrofit 对象
简介
Retrofit 通过使用方法上的『注解』来定义请求的构成,将我们声明的 Http 接口转化成一个 Call 对象。
这个 Call 对象呢,我们上周提到过,可以调用同步或非同步方法来发送请求,之后就交给 OkHttp 去执行啦。
使用
Retrofit 类用到了创建者模式,我们需要使用 Retrofit.Builder 来创建它的实例,接着调用 Retrofit.create(Class) 方法就能够生成我们的接口实现类了。
这里回顾一下 Retrofit 相关的使用:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
MyApi api = retrofit.create(MyApi.class);
Retrofit.Builder
Retrofit.Builder 是 Retrofit 对象的一个嵌套类,负责用来创建 Retrofit 实例对象,使用『建造者模式』的好处是清晰明了可定制化。
在执行 .build() 方法前,只有 .baseUrl() 是必须调用来设置访问地址的,其余方法则是可选的。
首先看一下 Builder.build() 最后的返回语句:
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
这里的参数包括了 Call 工厂,地址,转换器,CallAdapter 工厂, 执行 Callback 的线程池以及 validateEagerly 标识。
下面我们挑选其中几个参数来进行分析:
baseUrl
baseUrl 其实是 okHttp3 的 HttpUrl 类实例,一个 http 或者 https 协议的 URL。
为 Retrofit.Builder 添加 baseUrl,有两个重载的方法 baseUrl(String baseUrl) 和 baseUrl(HttpUrl baseUrl),但实际最后调用的都是后者。
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;
}
可以看到,检查验证后就设置了 Retrofit 对象的 URL。
callbackExecutor
callbackExecutor 是 Callback 调用中用于执行 Callback 的 线程池。
如果不自行设置的话,会根据平台设置一个默认的 Executor。
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
这里的 .defaultCallbackExecutor() 是 Platform 抽象类的一个方法。包含了 Converter,Client 等属性。他有三个实现类:Android,Java8,IOS。分别设置了各个平台下的一些默认参数。
在创建 Retrofit.Buidler 时会获取并设置当前环境的 Platform:
public Builder() {
this(Platform.get());
}
最后我们找到 Platform 的安卓实现类看一下:
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
了解过 Handler 机制的同学肯定十分眼熟,这里获取了主线程的 Looper 并构造了一个 主线程的 Handler,于是在 Android 平台,调用 Callback 时会将该请求 post 到主线程上去执行。
validateEagerly 标识
validateEagerly 是一个布尔类型的参数
我们知道当我们调用接口方法时,代理类会为方法创建一个 ServiceMethod。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
...
}
如果将 validateEagerly 标识设置为 True,那么在我们调用 .eagerlyValidateMethods(service) 方法之前就提前验证并创建好啦。
以上便是 Retrofit.Builder 的一些参数和方法,更具体的大家可以参照官方文档来学习。
.create 方法
现在我们通过嵌套类 build 了一个 Retrofit 对象,就可以开始执行下一步了。
// 将 Http 接口 转化为 Call 对象
MyApi api = retrofit.create(MyApi.class);
我们先直接把 create 方法的代码丢上来,代码并不是很多:
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);
}
});
}
下面一步步进行分析:
Utils.validateServiceInterface(service);
validateServiceInterface(service) 会验证我们的 Http 接口是否是 Interface,是否未包含了其他的接口。若为否则会抛出错误。
if (validateEagerly) {
eagerlyValidateMethods(service);
}
validateEagerly 的标签的作用则在之前已经说过了,算是一个提前验证标识。
接下来便返回了一个动态代理,其实仔细看会发现这里只是返回了动态代理的实例方法而已:
return (T) Proxy.newProxyInstance(...);
代理类首先获取了当前的平台 Platform,然后当你调用接口方法时,会调用到代理类的 invoke 方法。
我们看看 invoke 方法里到底做了什么:
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
如果我们调用的是来自 Object 类或者平台默认的方法,则会交给方法执行或者平台执行,但从代码上看 isDefaultMethod(method) 直接返回的是 false,可能是为了方便开发者扩展设置各个平台的不同方法调用。
ServiceMethod serviceMethod = loadServiceMethod(method);
经过两个判断后,会将我们的方法转换成一个 ServiceMethod 对象,我们可以来看看 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;
}
代码很简单,每个 Method 对应一个 ServiceMethod,如果缓存里没有,则新建一个。至于这个 ServiceMethod 是什么呢?我们具体可能要以后再详细分析。
这里简单的了解一下:之前我们说 Retrofit 对象的作用是将我们声明的 Http 接口转化成一个 Call 对象。实际上真正的工作是由 ServiceMethod 的来完成的,在其内部分析并转换了我们自定义的注解,并生成了一个 Call 对象。
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
接下来创建了一个 OkHttpCall。并使用 serviceMethod.CallAdapter 对 OkHttpCall 进行了转化。
我们在创建 serviceMethod 时,传入了 Retrofit 对象作为参数,这个 CallAdapter 就是从我们最开始构建 Retrofit 时所添加的 CallAdapterFatory所生成的。如果你没有设置的话,在 Android 平台,系统会为你设置一个 ExecutorCallAdapterFactory。
ExecutorCallAdapter会先返回一个 CallAdapter 实现类,.adapt(okHttpCall) 就是这个类的方法。
终于,callAdapter.adapt 把 okHttpCall 转化成了 ExecutorCallbackCall:
@Override public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
于是我们就完成了 .create() 方法的调用,实际上 Retrofit 的使用我们也几乎掌握了,因为之后的事情是交给 okHttp 去做的。
我们可以看看这个 ExecutorCallbackCall<>(callbackExecutor, call),参数里的 callbackExecutor,有没有很眼熟,之前 Retrofit.Builder 我们提到的默认添加的 Executor,这里其实就是我们 APP 应用的主线程。
也就是我们的网络请求完成后 Callback 回调的 onResponse 和 onFailure 方法,都会 post 到主线程上的 Handler 来执行。
总结
似乎这次文章的内容有点长?总结一句话就是:Retrofit 如何将 Http 接口方法调用转换成一个 Call 请求类。
这次我在学习代码和写文章到最后时,确实发现了之前的许多错误。目前难免会有许多理解不到位的地方,文章也写的比较散乱,希望各位能多多提出意见。
参考资料
[旧][Android] Retrofit 源码分析之 Retrofit 对象的更多相关文章
- [旧][Android] Retrofit 源码分析之 ServiceMethod 对象
备注 原发表于2016.05.03,资料已过时,仅作备份,谨慎参考 前言 大家好,我又来学习 Retrofit 了,可能这是最后一篇关于 Retrofit 框架的文章了.我发现源码分析这回事,当时看明 ...
- Retrofit源码分析(一)
1.基本用法 创建接口 public interface GitHubService { @GET("users/{user}/repos") Observable<List ...
- Appium Android Bootstrap源码分析之启动运行
通过前面的两篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>和<Appium Android Bootstrap源码分析之命令解析 ...
- Appium Android Bootstrap源码分析之命令解析执行
通过上一篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在b ...
- Appium Android Bootstrap源码分析之控件AndroidElement
通过上一篇文章<Appium Android Bootstrap源码分析之简介>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一个初步的 ...
- Android HandlerThread 源码分析
HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我又有一 个耗时任务需要执行,我们不得不重新创建线 ...
- Android Choreographer 源码分析
Choreographer 的作用主要是配合 Vsync ,给上层 App 的渲染提供一个稳定的 Message 处理的时机,也就是 Vsync 到来的时候 ,系统通过对 Vsync 信号周期的调整, ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号
Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...
随机推荐
- js监听url的hash变化和获取hash值
当浏览器浏览器的url进行变化时,浏览器默认是会去服务器将相应的资源给请求下来的,在不阻止默认行为的前提下,使用给url加锚点的方式(hash模式),让浏览器不跳转. window.addEventL ...
- 【笔记】macos上部署thanos_receiver + thanos_query
为了方便起见,在mac笔记本上进行了测试 1.写一个发送数据的客户端 package main import ( "fmt" "io/ioutil" " ...
- npm 和 yarn 前端包管理工具
前言 前端开发逐渐工程化,npm作为我们的依赖管理工具起到十分重要的作用,本文就来总结一下 npm 和 yarn 相关知识点. 正文 1.什么是npm (1)node的包管理器(node packag ...
- WebGPU | 相关知识概述
首先看下WebGPU的目标: 同时支持实时屏幕渲染和离屏渲染. 使通用计算能够在 GPU 上高效执行. 支持针对各种原生 GPU API 的实现:Microsoft 的 D3D12.Apple 的 M ...
- pytest文档2-用例执行
用例设计原则 1.文件名以test_******.py文件和*******_test.py 2.以test_****开头的函数 3.以Test***开头的类 4.以test_*****开头的方法 5. ...
- sysctl内核参数
sysctl命令用来配置与显示/proc/sys目录中的内核参数.如果想使参数长期保存,可以通过编辑/etc/sysctl.conf文件来实现. -a 显示所有的系统参数 -p 从指定的文件加载系统参 ...
- QPS、TPS、并发用户数、吞吐量
1.QPS QPS Queries Per Second 是每秒查询率 ,是一台服务器 每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内 所处理流量多少的衡量标准, 即每秒的响应请求数,也 ...
- spring 异常处理的方式?
一.使用SimpleMappingExceptionResolver解析器 1.1在mvc中进行 配置. <?xml version="1.0" encoding=" ...
- String Reversal
Educational Codeforces Round 96 (Rated for Div. 2) - E. String Reversal 跳转链接 题目描述 定义一个操作为交换字符串中相邻的两个 ...
- HowToDoInJava Spring 教程·翻译完成
原文:HowToDoInJava 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. ApacheCN 学习资源 目录 Spring 5 Spr ...