引言
Retrofit 是 Square 公司开发的一款针对 Android 网络请求的框架,遵循 Restful 设计风格,我们查看 invoke 可以发现:底层基于 OkHttp 实现的 ,相比其他网络请求框架,有以下优势:
- 性能最好,速度最快(动态代理优势)
- 简洁易用,代码简化
- 解耦彻底,职责细分
- 易与其他框架联用(Rxjava)
当然也有弊端:因为高度封装,相对于 OkHttp ,扩展性变差了,不过整体能满足项目需求.今天我就带大家来看一下 Retrofit 究竟是如何使用,以及内部工作原理
一.概述
作用
OKHttp 做的更加简洁,更加方便,同时影藏了 oKHttp 的某些功能。
type-safe
retrofit 是基于 okhttp 的,因此 retrofit 所有的工作都是围绕在请求体和响应体来展开的,Retrofit 提供了各种类型的转换器以及可以自定义转换器,去构建你的请求体,以及通过转换器去序列化响应体为你想要的类型,从而保证你的请求体和响应体都是安全的
二.使用
1. 添加 retrofit 依赖
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
2. 添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>
3. 创建实体类
访问 小木箱 github 仓库,通过 get 请求得到了以下报文:
然后,通过 Gsonformat 得到相关实体类对象:
class MicroKibacoRepo {
private int id;
private String node_id;
private String name;
private String full_name;
// ---为了避免浪费篇幅,省略无用代码---
}
4. 创建⼀一个 interface 作为 Web Service 的请求集合,在⾥里⾯⽤注解(Annotation)写⼊需要配置的请求方法
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<MicroKibacoRepo>> listRepos(@Path("user") String user);
}
5. 在正式代码里⽤ Retrofit 创建出 interface 的实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
6. 调⽤创建出的 Service 实例的对应⽅法,创建出相应的可以⽤来发起网络请求的 Call 对象
Call<List<MicroKibacoRepo>> repos = service.listRepos("MicroKibaco");
7. 使用 Call.execute() 或者 Call.enqueue() 来发起请求
repos.enqueue(new Callback<List<MicroKibacoRepo>>() {
@Override
public void onResponse(Call<List<MicroKibacoRepo>> call, Response<List<MicroKibacoRepo>> response) {
}
@Override
public void onFailure(Call<List<MicroKibacoRepo>> call, Throwable t) {
}
});
三. Retrofit 常用 API
方法 | 作用 |
serviceMethodCache | 自定义的接口映射对象集合 |
baseUrl | 请求地址 |
callFactory | 默认为 OKHttpCall |
converterFactories | 数据解析器工厂集合 |
callbackExecutor | 回调执行,Android 平台默认为 MainThreadExecutor |
Platform | Retrofit 中用来管理多平台的方法,支持 Android、Java8。通过 findPlatform 获取对应的平台,同时也初始化了 defaultCallAdapterFactory 工厂 |
ServiceMethod | 接口映射的网络请求对象,通过动态代理,将自定义接口的标注转换为该对象,将标注及参数生成 OkHttp 所需的 Request 对象。Retrofit 的 create 通过动态代理拦截,将每一个自定义接口转换成为一个 ServiceMethod 对象,并通过通过 serviceMethodCache 进行缓存 |
ExecutorCallAdapterFactory | 回调执行,Android 平台默认为 MainThreadExecutor |
CallAdapter.Factory | CallAdapter 的静态工厂,包含 get 的抽象方法,用于生产 CallAdapter 对象 |
OkHttpCall | OkHttpCall 的 Call 实现,通过 createRawCall 得到真正的 okhttp3.Call 对象,用于进行实际的网络请求 |
Call | Retrofit 定义的网络请求接口,包含 execute、enqueue 等方法 |
Converter.Factory | 数据解析器工厂,用于生产 Converter 实例 |
ExecutorCallAdapterFactory | Android 平台默认的 CallAdapter 工厂,get 方法使用匿名内部类实现 CallAdapter,返回 ExecutorCallbackCall,实现了 CallExecutorCallbackCall |
ExecutorCallbackCall | 采用静态代理设计,delegate 实际为 OkHttpCall,使用 callbackExecutor 实现回调在主线程中执行 |
RxJavaCallAdapterFactory | Rxjava 平台的 CallAdapter 工厂,get 方法返回 RxJavaCallAdapter 对象 |
RxJavaCallAdapter | Rxjava 平台的设配器,返回 observable 对象 |
GsonConverterFactory | 数据解析工厂实例,返回了 GsonResponseBodyConverter 数据解析器 |
GsonResponseBodyConverter | Gson 的数据解析器,将服务端返回的 json 对象转换成对应的 java 模型 |
四.源码解析
读源码如何开始呢?
我们先带着问题看源码,Retrofit 有几个关键的流程
- Retrofit 如何将定义的 interface 转换成网络请求?
- Retrofit 的 Converter 机制是如何实现?
- Retrofit 的 CallAdapter 机制是如何实现?
1. 寻找入口
一行一行读,肯定是不可行的,太累了,而且脑容量不够,记不住,合适的读源码方式是从程序入口开始入手。当前功能开始读。
当前请求的入口发生在enqueue, 那么,我们就开始从这个 Api 入手读源码.
enqueue, 是进入队列的意思,进去查看,竟然是个
void enqueue(Callback<T> callback);
如果出现类似抽象方法;就得往回看了,因为读不下去了
2. 寻找接口实现
所以我们往上面找,上面的 Api 是 enqueue,去找 相关接口实现,找了半天,爱哭,接口实现是 GithubService
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<MicroKibacoRepo>> listRepos(@Path("user") String user);
}
而 GithubService.java 是我自己创建的,怎么办呢? 继续往回看,找呀找, 找到了 Retrofit 的初始化方法 create,整个源码我就不翻出来了,翻一翻关键的部分,其中需要讲述的 APi 有:
- eagerlyValidateMethods
- newProxyInstance
- Platform
- invoke
- invokeDefaultMethod
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T)
// --------动态代理--------
Proxy.newProxyInstance(
// 提供动态类的实例
service.getClassLoader(),
// 获取 GithubService 的实例,获取需要动态代理类的接口,在 retrofit.create 传入
new Class<?>[] { service },
//
new InvocationHandler() {
// 判断接口平台,是JDK 7.0/8.0 或者 Android/IOS
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// 如果这个方法不是直接声明的,那么就直接调用了,兼容性,保护性,不至于越权
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// java 8 开始,接口运行有默认的方法实现了
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
static <T> void validateServiceInterface(Class<T> service) {
// 1. 验证 GitHubService 是否为一个接口
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
// 2.验证 GitHubService 是否继承其他接口,只接受原生接口
if (service.getInterfaces().length > 0) {
throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
}
}
// 3. 在被初始化的时候,是有一个初始化过程,会去读注解,中间是有耗时的,分布式加载网络请求, validateEagerly 是方便调试的
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
Platform.java 看一下相关实现,里面包含两个平台一个是 Java8 还有一个 Android,这个 APi 主要是平台标识,知道就行了,没什么好说的
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
// Android 平台
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
// Java8 平台
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
loadServiceMethod 里面也没有 invoke,我们通过 parseAnnotations 去找 一下 invoke 试试看能否找到,
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);
}
}
return result;
}
ServiceMethod.java里面,不过也是一个抽象方法 ,于是我们往回看 parseAnnotations,看里面是否有 invoke 的逻辑实现,找到了,好家伙,parseAnnotations 是 ServiceMethod 子类 HttpServiceMethod 实现方法,而 HttpServiceMethod 一定有 invoke 实现
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
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) {
throw methodError(method, "Service methods cannot return void.");
}
// 通过 parseAnnotations 去寻找 invoke实现
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract T invoke(Object[] args);
}
不用说,我直接在 HttpServiceMethod.java, ctr + f 搜,便搜到了invoke,原来底层真是通过 OKHttp 实现的:
final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
// ----为了避免浪费篇幅,省略若干无用代码----
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
}
上面我说过,看到抽象方法,往回看 ,所以我们来看 okHttpCall,OKHttpCall 实现了 Call 接口 ,重点说一下一下方法:
- createRawCall
// OKhttp3的Call帮助Retrofit实现网络请求的Call
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
- call.enqueue
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
// response 结果交给 Retrofit 使用
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
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();
}
}
});
}
什么是动态代理?
动态代理是创建一个对象,这个对象传入一个接口并且帮你实现每一个接口的每一个方法,并且这每一个接口的每一个方法都会指向每一个接口每一个方法的 invoke 方法。
- callAdapter
HttpServiceMethod.java里面的构造方法, 我们找到了 CallAdapter,然后去里面查看是通过 createCallAdapter返回的
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method) {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
try {
// CallAdapter 创建
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
进入 Retrofit.java 查看一下:
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
// 通过 callAdapterFactories 获取 callAdapter
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
// ----为了避免浪费篇幅,省略若干无用代码----
}
callAdaperFactory是通过 Retrofit 的 build 方法构建而来.
public Retrofit build() {
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
我们看到 callAdaperFactory 通过 addAll 一次性添加过来,所以有我们看一下defaultCallAdapterFactories方法.
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
return Build.VERSION.SDK_INT >= 24
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
然后我们再看一下 ExecutorCallAdapterFactory
五. 面试题分享
Retrofit 中的设计模式
1. 建造者模式
Retrofit 对象的创建、ServiceMethod 对象创建都使用 Build 模式,将复杂对象的创建和表示分离,调用者不需要知道复杂的创建过程,使用 Build 的相关方法进行配置创建对象。
2. 外观模式
Retrofit 对外提供了统一的调度,屏蔽了内部的实现,使得使用该网络库简单便捷。 门面模式: 提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口。使用子系统更容易使用
3. 动态代理模式
通过动态代理的方式,当调用 Retrofit 的 create()方法时,会进行动态代理监听。当执行具体的接口方法时,会回调 InvocationHandler。通过反射解析 method 的标注及参数,生成 ServiceMethod 对象。
4. 静态代理模式
Android 平台默认的适配器 ExecutorCallbackCall,采用静态代理的模式。具体的实现 delegate 为 OkHttpCall。
5. 工厂模式
Converter 及 CallAdapter 的创建都采用了工厂模式进行创建。
6. 适配器模式
CallAdapter 的 adapt 采用了适配器模式,使得 interface 的返回对象可以动态扩展,增强了灵活性
CallAdapter 的种类
Converter 种类
Retrofit 支持多种数据解析方式,使用时需要在 Gradle 添加依赖。
Retrofit 流程总结