备注

原发表于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 请求类。

这次我在学习代码和写文章到最后时,确实发现了之前的许多错误。目前难免会有许多理解不到位的地方,文章也写的比较散乱,希望各位能多多提出意见。

参考资料

Retrofit 2.0.0 API

快速Android开发系列网络篇之Retrofit

[旧][Android] Retrofit 源码分析之 Retrofit 对象的更多相关文章

  1. [旧][Android] Retrofit 源码分析之 ServiceMethod 对象

    备注 原发表于2016.05.03,资料已过时,仅作备份,谨慎参考 前言 大家好,我又来学习 Retrofit 了,可能这是最后一篇关于 Retrofit 框架的文章了.我发现源码分析这回事,当时看明 ...

  2. Retrofit源码分析(一)

    1.基本用法 创建接口 public interface GitHubService { @GET("users/{user}/repos") Observable<List ...

  3. Appium Android Bootstrap源码分析之启动运行

    通过前面的两篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>和<Appium Android Bootstrap源码分析之命令解析 ...

  4. Appium Android Bootstrap源码分析之命令解析执行

    通过上一篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在b ...

  5. Appium Android Bootstrap源码分析之控件AndroidElement

    通过上一篇文章<Appium Android Bootstrap源码分析之简介>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一个初步的 ...

  6. Android HandlerThread 源码分析

    HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我又有一 个耗时任务需要执行,我们不得不重新创建线 ...

  7. Android Choreographer 源码分析

    Choreographer 的作用主要是配合 Vsync ,给上层 App 的渲染提供一个稳定的 Message 处理的时机,也就是 Vsync 到来的时候 ,系统通过对 Vsync 信号周期的调整, ...

  8. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  9. Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号

    Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...

随机推荐

  1. 微服务架构 | *3.5 Nacos 服务注册与发现的源码分析

    目录 前言 1. 客户端注册进 Nacos 注册中心(客户端视角) 1.1 Spring Cloud 提供的规范标准 1.2 Nacos 的自动配置类 1.3 监听服务初始化事件 AbstractAu ...

  2. WebGPU 中消失的 VAO

    1 VAO 是 OpenGL 技术中提出来的 参考: 外链 其中有一段文字记录了 VAO 是什么: A Vertex Array Object (VAO) is an object which con ...

  3. golang中使用switch语句根据年月计算天数

    package main import "fmt" func main() { days := CalcDaysFromYearMonth(2021, 9) fmt.Println ...

  4. java ++ -- 异或 短路与 短路或 三目条件

    public class Demo { public static void main(String[] args) { int i1 = 12, i2 = 20; int i = (i2++); S ...

  5. 什么是协程(第三方模块gevent--内置模块asyncio)

    目录 一:协程 1.什么是协程? 2.携程的作用? 3.安装第三方模块:在命令行下 二:greenlet模块(初级模块,实现了保存状态加切换) 三: gevent模块(协程模块) 1.time 模式协 ...

  6. Python解释器下载安装教程

    简介: 自从20世纪90年代初Python语言诞生至今,它已被逐渐广泛应用于系统管理任务的处理和Web编程.2021年10月,语言流行指数的编译器Tiobe将Python加冕为最受欢迎的编程语言,20 ...

  7. linux主机名称文件修改

    目录 一:linux主机名称查看与修改 1.查看自己主机名: 3.临时修改 4.永久修改 一:linux主机名称查看与修改 1.查看自己主机名: [root@localhost ~]# echo $H ...

  8. python21day

    内容回顾 re模块的常用方法 findall(正则,待匹配字符串,flag):返回所有匹配项的列表 search:返回一个变量,通过group取到第一个匹配项 match:从头开始找第一个,其他同se ...

  9. python小兵 面向对象继承super和c3算法

    python多继承 在前面的学习过程中. 我们已经知道了Python中类与类之间可以有继承关系. 当出现了x是一种y的的时候. 就可以使⽤继承关系. 即"is-a" 关系. 在继承 ...

  10. Vue.js之计算属性(computed)、属性监听(watch)与方法选项(methods)

    vue.js官网:https://cn.vuejs.org/v2/guide/components-registration.html 一.计算属性-computed 1. 作用:能够避免数据冗余,通 ...