前言
使用Retrofit已经一段时间了,这货挺好用的,还很特别,特别是使用接口来定义请求方式,这用法让我对它的源码很是好奇。今天就来看看源码吧...
参靠源码retrofit:2.0.2
基本的用法
首先来简单得实现一次GET请求
- 定义接口
interface Service {
@GET("News")
Call<ResponseBody> getNews(
@Query("limit") String limit);
}
- 完成一次请求
Retrofit retrofit =new Retrofit.Builder()
.baseUrl("http://hcy.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.build();
Service service = retrofit.create(Service.class);
Call<ResponseBody> call = service.getNews("10");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
Log.i(TAG, "onResponse");
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, "onFailure");
}
});
根据这个请求一步步进行解析,接下来就是这篇的重点,Retrfit内部的实现。
还不会用Retrofit?少年去看看Retrofit 2.0 的使用吧!!!
源码解析
这里分别说明了都调用了哪些源码,都是怎么实现的。(里面涉及到一些设计模式,什么?你还不知道有什么设计模式?下面会涉及到Builder模式(外观模式)、工厂模式、动态代理。stay 的 Retrofit分析-经典设计模式)
-
1.Retrofit的创建
首先看下Retrofit对象的创建
Retrofit retrofit =new Retrofit.Builder()
.baseUrl("http://hcy.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.build();
这里使用了Builder设计模式(外观模式),Retrofit.Builder是Retrofit的一个内部类,用来配置一些成员变量,这里配置了baseUrl和ConverterFactory(对象的序列号/反序列化组件),然后创建一个Retrofit对象。这些代码都做了什么,下面一一说明。
-
Retrofit.Builder()
看看new Retrofit.Builder()调用的代码
public Builder() {
this(Platform.get());
}
Platform.get()又是什么?抱着一贯的好奇,点进去看看。主要代码如下
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("org.robovm.apple.foundation.NSObject");
return new IOS();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
机智的你一定能看出来,主要就是这个findPlatform()方法。这里面的代码,就是判断当前运行的平台。可以看到里面有Android、Java8、IOS。等下,怎会有IOS,什么鬼(为什么会有IOS就交给你去研究了)。
我们在Android上运行的话,就调用了return new Android()。进一步往下看,Android()是什么
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);
}
}
}
他继承了Platform重写了defaultCallbackExecutor和defaultCallAdapterFactory方法。
defaultCallbackExecutor:返回的是用于执行 Callback 的 线程池。可以看到MainThreadExecutor 获取了主线程的 Looper 并构造了一个主线程的 Handler,调用 Callback 时会将该请求 post 到主线程上去执行。这就解释了为什么请求后完成的回调都是在主线中。
defaultCallAdapterFactory:将返回的适配类型默认为Call类型(如果使用RxJava的话,就可以通过配置.addCallAdapterFactory(RxJavaCallAdapterFactory.create())将配置类型改成Observable。)
- .baseUrl("http://hcy.com/api/")
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;
}
这里有两个重载的方法,创建了okhttp3 的 HttpUrl 实例。
- .addConverterFactory(GsonConverterFactory.create())
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
往转换工厂集合中添加了我们指定的转换工厂,最后将返回的数据类型转换成对应的实体类对象的Converter类型。在我们的例子里面 GsonConverterFactory 将选用 GsonConverter 来转换。
- .build();
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// 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);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
看最后一句
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
这里才是创建Retrofit对象的地方,之前的只是一些配置。里面的参数:
callFactory(Call工厂):看到了吧callFactory = new OkHttpClient();,这里用的是okhttp3;
baseUrl(服务器基本地址):这个我们上面配置过;
converterFactories(对象的序列号/反序列化组件):我们上面配置过。
adapterFactories(适配类型)、callbackExecutor(执行 Callback 的线程池):从我们上面提到的platform中获取默认值。
validateEagerly(标识):先不说,后面会用到
- 总:完成基本的配置,创建一个Retrofit对象
2.Service的创建以及接口的调用
我们创建了一个接口的实例,用于调用接口。看下代码吧
Service service = retrofit.create(Service.class);
Call<ResponseBody> call = service.getNews("10");
Service是之前定义的接口,这里代码通过retrofit.create(Service.class)就得到了Service的实例,他是怎么做到的?进入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);
}
});
}
看到这里的代码,相信有些同学开始懵逼了,Proxy.newProxyInstance...这是什么鬼?这代码鬼认识...
哈哈,这叫动态代理,可以生成接口对应的对象,之后使用这个对象调用方法时都会调用InvocationHandler中的invoke方法。(我不会告诉你们我一开始也是懵逼的~~)
对动态代理还不熟悉的看看这里: 公共技术点之 Java 动态代理
下面我们来一步步分析这个create方法:
-
Utils.validateServiceInterface(service);
源码我就不贴出来了,这个方法主要就是判断了参数service是否为Interface,是否包含了其他接口; -
eagerlyValidateMethods(service);:
这里根据validateEagerly判断是否需要提前创建ServiceMethod,调用loadServiceMethod()方法,这个方法我们自后面会讲到。 -
invoke
接下来看Proxy.newProxyInstance中重写的方法invoke,这才是这次解析的重点。看看里面都做了什么- 代码
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
第一个if用来判断的是否为Object方法,如果是就直调用;
第二个if则是判断平台,不过进入.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;
}
loadServiceMethod做了三件事:
1.先去serviceMethodCache中查找否存在method(看来这货是有缓存的,这里采用了LinkedHashMap来缓存这些Method的解析结果),存在的话跳过第二步;
2.method不存在的话就创建一个,然后添加到缓存中;
3.返回ServiceMethod 对像。
可以看到ServiceMethod也使用了Bulider设计模式,继续往里面看?看看new ServiceMethod.Builder(this, method).build();都干嘛了?
这里就简单说说ServiceMethod的功能,再讲下去这层次结果有点深...
ServiceMethod的定义:把对接口中的方法的调用转化成一次HTTP调用。
(说人话...)
呃...,就是解析了接口中@GET("News")、@Query("limit") String limit等一些列有关请求的信息,然后还保存了Retrofit中的一些重要信息,如:
1、callFactory:Call工厂,负责创建 HTTP 请求
2、callAdapter:确定返回的retrofit2.Call<T>类型(接口定义时的返回类型,例子中的Call<ResponseBody>);
3、responseConverter:数据转换类型,负责将服务器返回的数据(Json、xml等各式)转换成我们需要用到的T类型的对象;
4、parameterHandlers:则负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数。(如例子中的@Query("limit")中的limit)
总之就是基本包含了这次请求的全部内容
-
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
创建了一个OkHttpCall 对象,用来发起请求。OkHttpCall 中有个我们常用的方法enqueue()
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
....
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call,
okhttp3.Response rawResponse) throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
...
});
}
都说Retrofit内部是用okhttp实现请求的,原来在这里,哈哈...
拿着这个对象我们就可以发起请求了,不过Retrofit还要适配返回类型,所以还要下面这句代码。
-
return serviceMethod.callAdapter.adapt(okHttpCall);
这里将我们创建的OkHttpCall 对象适配成对应的类型(例子中得到的是Call,如果用RxJava得到的就是Observable)。
3.发起请求
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
Log.i(TAG, "onResponse");
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, "onFailure");
}
});
最后就是使用得到的call对象来发起请求(这个call是retrofit2.Call)。
通过上面的解析可以知道,这里其实就是调用了okhttp3里面的okhttp3.Call来完成这次请求。
还不满足?想知道okhttp3是怎么完成请求的?
自己去研究吧,少年~~
以上有错之处,还望不吝赐教。谢谢!!
参考
拆轮子系列:拆 Retrofit
Retrofit源码1: 为什么写一个interface就可以实现http请求
Retrofit2 源码解析
Retrofit 源码分析之 Retrofit 对象
Retrofit分析-漂亮的解耦套路
Retrofit分析与实现
网友评论