前言
之前讲过了okhttp的超级概括的原理解析,okhttp以它优秀的线程池设计,任务队列的分配和转化以及基于责任链模式设计的5大拦截器的使用被广大开发者间接引入。等等,为啥是间接?你点进来的时候相信已经有了答案,当然是因为Retrofit的横空出世。当我们使用okhttp的时候肯定得去封装一层,封装的时候得考虑到主、子线程的切换,api返回值的转化处理等等。当你哼哧哼哧或者嘻嘻哈哈封装完毕后,某个小伙伴说他用不习惯,他更愿意去做成XXXX+YYYY+okhttp的模式。这个时候你是选择把他k.o.还是自己默默按照他的方式再来一套呢?当然是选择retrofit啦,所以retrofit还是一个增进队友之间友谊的软工具。接下来让我们一步一步的深挖这个优秀的开源框架是怎么让友谊的小船越走越远的。
使用
public class MainActivity extends AppCompatActivity {
public static final String API_URL = "https://api.github.com";
public void request(View view) {
Retrofit retrofit =
new Retrofit.Builder()
.client(new OkHttpClient())
.callFactory(new OkHttpClient())
.baseUrl(API_URL)
//.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//.addConverterFactory(GsonConverterFactory.create())
.build();
// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);
// Create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");
// Fetch and print a list of the contributors to the library.
new Thread(new Runnable() {
@Override
public void run() {
try {
List<Contributor> contributors = call.execute().body();
Log.e("ss", contributors.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
request(null);
}
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
public static class Contributor {
public final String login;
public final int contributions;
public Contributor(String login, int contributions) {
this.login = login;
this.contributions = contributions;
}
@Override
public String toString() {
return "Contributor{" +
"login='" + login + '\'' +
", contributions=" + contributions +
'}';
}
}
}
以上代码是从官网的sample中拷贝的一段,除了线程那边,其他差不多都一样,把代码原样拷贝到as项目中是可以直接运行的。使用上就说到这里(然而并没有说啥)。相信大家对Retrofit的使用早就烂熟于心,甚至看完我写的样例代码后,起身怒拍桌子。所以这里就不多赘述,我们开始撕源码。
retrofit的封装模式其实就是将okhttp覆盖了一层更加友好的调用方式,它本质还是得靠okhttp来执行网络请求,就像一把剑,如果单有锋利的剑身,没有剑柄和剑鞘,虽然也能杀敌,但是首先得自损八百。当照顾到使用者习惯后加上了剑柄和剑鞘,甚至再付个魔,使用起来肯定事半功倍,但是真正起作用的只有剑身。retrofit和okhttp的爱恨纠葛大概说起来就是下面这张图:
读源码最简单的方式就是从使用上入手,所以,我们就从上面的样例代码中开始,看看Retrofit的工作流程。
Retrofit.build()方法究竟做了什么
retrofit使用的是构建模式创建实例,我们重点看下它最终build的时候做了哪些事情
-
判断是否有baseUrl
if (baseUrl == null) { throw new IllegalStateException("Base URL required."); }
-
赋值callFactory,这个callFactory其实就是okhttpClient
okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); }
如果没有值就默认给OkHttpClient,使用Retrofit.client(okhttpClient)也是给callFactory赋值
/** * The HTTP client used for requests. * * <p>This is a convenience method for calling {@link #callFactory}. */ public Builder client(OkHttpClient client) { return callFactory(Objects.requireNonNull(client, "client == null")); } /** * Specify a custom call factory for creating {@link Call} instances. * * <p>Note: Calling {@link #client} automatically sets this value. */ public Builder callFactory(okhttp3.Call.Factory factory) { this.callFactory = Objects.requireNonNull(factory, "factory == null"); return this; }
-
赋值callbackExecutor
Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); }
通过上面的代码可以看到,如果没有设置,就默认给出platform.defaultCallbackExecutor(),那这个默认又个是啥?callbackExecutor究竟是干嘛用的?我们点进去康康
class Platform { ... @Nullable Executor defaultCallbackExecutor() { return null; } ... }
Platform,顾名思义,就是平台,我们初步判断这个类其实就是选择平台用的,但是return null???,直觉告诉我事出反常必有妖,肯定是哪里把这个方法给重写了。
static final class Android extends Platform { ... @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } ... }
果然是这样,是一个叫Android的类复写了这个方法,那是在哪里对这个Platform进行赋值了呢?
public static final class Retrofit.Builder { ... public Builder() { this(Platform.get()); } ... }
其实就是在创建Retrofit的Builder的时候就已经对所选平台进行了赋值,具体的赋值方法如下
class Platform { private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { return "Dalvik".equals(System.getProperty("java.vm.name"))//就是这一行代码确定了平台 ? new Android() // : new Platform(true); } ... }
确定了平台后,再看看Android平台下是怎么赋值defaultCallbackExecutor的
static final class Android extends Platform { ... @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } ... static final class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } } }
defaultCallbackExecutor方法就是构造了一个MainThreadExecutor的实例,这个实例里面有个new Handler(Looper.getMainLooper());很熟悉有木有,看到这里我们大概能猜出来了,Retrofit.callbackExecutor这个成员变量其实就是一个用来切换线程的东东,在执行了execute方法后会切换到主线程。
-
赋值callAdapterFactories
// Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
上述代码中callAdapterFactories,就是一个工厂模式,这里是做成了列表,表示能创建不同的callAdapter,这个callAdapter其实就是类比到okhttp中的Call,网络请求的关键一步(call.equeue(),call.execute())就是由这个来做的(先这么认为,建立一个概念)。这里要看看最后那行platform.defaultCallAdapterFactories(callbackExecutor),是不是感觉很熟悉,这里其实就是做了两件事情,第一步:把你之前在Retrofit.addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()中添加的RxJava2CallAdapterFactory这个实体给放到callAdapterFactories列表中。第二步:将平台中默认的callAdapterFactory也放在这个列表中。那么平台默认的AdapterFactory长啥样?
List<? extends CallAdapter.Factory> defaultCallAdapterFactories( @Nullable Executor callbackExecutor) { DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor); return hasJava8Types ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory) : singletonList(executorFactory); }
这个方法中也做了两件事,首先创建一个平台默认的callAdapterFactory(DefaultCallAdapterFactory),然后判断hasJava8Types,如果为false,就将这个DefaultCallAdapterFactory放入到列表中并返回。使用singletonList方法是使用定长列表,为了减少内存分配(要不人怎么能去写开源框架呢,什么事情都做到极致👍——暗示点赞)。如果为true那就除了DefaultCallAdapterFactory外还要额外加一个CompletableFutureCallAdapterFactory,那hasJava8Types是何时被确定下来的呢?
class Platform { ... Platform(boolean hasJava8Types) { this.hasJava8Types = hasJava8Types; ... } }
上述代码得知,是通过构造方法确认的,所以要去Android类下看看Android是怎么确定这个值的
static final class Android extends Platform { Android() { super(Build.VERSION.SDK_INT >= 24); } ... }
很简单,只有当sdk>=24时才为true,为什么要做这样的一个判断?因为java1.8有一个新特性叫CompletableFuture,这里不去深挖了,感兴趣的同学可以狗哥一下(狗哥一下,就是强大👍——暗示点赞)。
-
赋值converterFactories
List<Converter.Factory> converterFactories = new ArrayList<>( 1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize()); // Add the built-in converter factory first. This prevents overriding its behavior but also // ensures correct behavior when using converters that consume all types. converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); converterFactories.addAll(platform.defaultConverterFactories());
converterFactories就是给服务端的返回值进行一个转化用的,这里面的代码做了几件事情。
- 创建一个List<Converter.Factory> converterFactories列表,这个列表的长度设置成在build中配置的converterFactories的长度加平台中的默认ConverterFactories的长度+1
- 添加BuiltInConverters转换工厂,这里给出了注释,狗哥翻译:首先添加内置转换器工厂。 这可以防止覆盖其行为,但也可以确保使用使用所有类型的转换器时的正确行为。大概意思就是个内置转换器,用来处理一些奇葩的返回,比如Stream,Buffer,Void,甚至kotlin中的Unit
- 添加用户自定义的转换工厂
- 添加平台中的转化工厂,这个和第一步中的platform.defaultConverterFactoriesSize()平台工厂列表长度相呼应,如果平台中有转换工厂列表就有size,如果没有size 就等于0。
平台默认工厂列表有哪些?看代码
class Platform { ... List<? extends Converter.Factory> defaultConverterFactories() { return hasJava8Types ? singletonList(OptionalConverterFactory.INSTANCE) : emptyList(); } int defaultConverterFactoriesSize() { return hasJava8Types ? 1 : 0; } ... }
我们看到了熟悉的hasJava8Types,这个就不多说了,如果为true的时候会添加一个这个工厂,同时返回的defaultConverterFactoriesSize会等于1,和上一段代码相呼应。
-
在一切都准备就绪后,一个Retrofit实例就成功创建出来了
return new Retrofit( callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); }
上面代码中的unmodifiableList,其实是Collections.unmodifiableList()这个方法的大概意思就是,你不能这样去添加一个factory工厂:Retrofit.getConverterFactories().add(......),如果这么做就会报错。这样写的意图是为了代码的健壮性。
前菜结束,下面来几个正餐
Retrofit.create()方法究竟做了什么
直接上代码
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable 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);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
唉咦!!Proxy.newProxyInstance,乍一看仿佛又看到了熟悉的东西,仔细一看,还确实是动态代理,代理就是将某个对象的一些方法交给第二个对象(代理对象)去完成。举一个很形象的例子。
特能诌公司产了型号为never-stop-740的电瓶车(以下简写740电瓶车),这款740电瓶车根本刹不住车,N女士当时不知道有问题,买回来后出了事情,去特能诌公司讨要说法,但是无果,这时候N女士需要一个更加强大的人来代替她去要说法。
于是她就委托X律师全权处理刹车失灵的事情。X律师一开始也和N女士一样去特能诌先去讨要说法,但是无果,这个时候X律师就想办法要到了740电瓶车的出厂清单,然后找专业人士去分析这个清单,最后实锤740电瓶车确实是因为技术问题导致刹车失灵,然后X律师拿着740电瓶车刹车失灵的清单报告将特能诌公司告上了法庭,在充足的证据面前特能诌不得不对N女士进行了道歉,X律师完成了N女士的讨回公道的请求。
我们看看N女士的整个过程,首先N女士有一个“讨回公道”的要求,但是因为特能诌比较能诌,显然N女士自己不能完成,于是她就找了代理X律师,X律师代理后首先也是去特能诌讨说法(和N女士一样),但是X律师的手段比N女士多得多,又是联系厂商出报告,又是找专业人士分析报告,又是将特能诌告上法庭,最终完成了N女士“讨回公道”的需要。
从上面的例子中可以看出代理的作用就是将原来实体可以实现的给实现掉,甚至还能把原来实体的功能进行增强,从代码中看
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
这里的GitHub类可以看成是N女士,她有一个诉求就是执行contributors方法,获取到git的仓库列表。显而易见,你GitHub只不过是一个小小接口,啥都干不了,你还想去通过网络请求去获取git仓库列表?真是小小接口,可笑可笑。就像是N女士面对特能诌讨说法束手无策一样。但是这时候GitHub接口去找到了动态代理Proxy.newProxyInstance(...),这个动态代理可以看成是X律师,这个动态代理可以把GitHub接口中配置的东西(@Get()中的api,contributors方法中的各种请求需要的参数)全部转化为okhttp需要的参数,然后顺便再将请求通过okhttp发送出去,甚至当数据返回时还能帮忙把数据解析成想要的格式。这真是“山重水复疑无路,柳暗花明又一村”啊。
那为什么要选择动态代理呢,如果是选择静态代理,那GitHub有多少种方法,在静态代理中就要增加多少个对应的方法。动态代理采用了反射机制,将所有的请求全部都流经这个动态代理的invoke()方法中,对于使用者来说如果你将来想要往里面添加请求,比如获取某个列表的详情,那只要像contributors方法一样添加一个声明类的方法即可,动态代理会帮你处理剩下的事情。对,没错,是耗性能,但是retrofit用了这么久,它不香吗?
接下来我们看看这个动态代理究竟帮助我们做了哪些重要的事情
首先要明确一个重要的概念
GitHub github = retrofit.create(GitHub.class);
上述代码执行完后其实只是创建了一个代理,下述代码中的InvocationHandler.invoke方法并不会马上被执行
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable 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);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);//代码30
}
});
}
那什么时候被执行?就是执行下述代码的时候
Call<List<Contributor>> call = github.contributors("square", "retrofit");
为什么呢?当GitHub github = retrofit.create(GitHub.class);这个代码执行后,实际上github 这个类已经不是它自己了,而是一个代理(X律师)。那代理总归是能代替原来的实体做事情的,它做的事情都会流经invoke。那原来的实体要做什么呢,就是执行contributors()方法。动态代理中的原理也需要深入源码去分析,现在只要知道只有在代理执行方法的时候才会执行invoke方法就行,以后会出一个动态代理源码分析的记录。而invoke()方法中的method,就相当于contributors()这个方法
明确了这一点后我们看看github(代理).contributors方法会执行哪些东西:
private final Platform platform = Platform.get(); 获取当前平台
-
return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args);
执行方法
其中这里首先会判断下当前需要执行的方法是不是默认方法,我们跟进去看一眼
boolean isDefaultMethod(Method method) { return hasJava8Types && method.isDefault(); }
hasJava8Types我们先不管,因为它true和false都有可能。跟进method.isDefault()方法
/** * Returns {@code true} if this method is a default * method; returns {@code false} otherwise. * * A default method is a public non-abstract instance method, that * is, a non-static method with a body, declared in an interface * type. * * @return true if and only if this method is a default * method as defined by the Java Language Specification. * @since 1.8 */ public boolean isDefault() { // Android-changed: isDefault() implemented using Executable. return super.isDefaultMethodInternal(); }
看注释,这又是一个java8的新特性,意思是如果被default修饰符修饰那就返回true,否则返回false。这里再多一嘴这个新特性
通常我们写接口类的方法时一般只能写它的类似声明的一个方法,而不能写具体的实现,但是java8中你一旦加入了这个default修饰符后,你就可以去写具体的实现。就像这样
interface MyInterface { default void eat() { System.out.println("I eat beef"); } }
意思是如果有个类你implement这个接口类,那你不去实现这个eat()方法,那我就调用默认的 System.out.println("I eat beef");
显然一般来说我们不可能多去写这么个东西,所以method.isDefault()肯定返回false,从而platform.isDefaultMethod(method) 这个肯定返回false,从而肯定会执行loadServiceMethod(method).invoke(args)这个方法,这个方法分两步走
第一步:loadServiceMethod(method),我们跟进去看看
public final class Retrofit { private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>(); ... ServiceMethod<?> loadServiceMethod(Method method) { ServiceMethod<?> result = serviceMethodCache.get(method);//代码1 if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = ServiceMethod.parseAnnotations(this, method);//代码2 serviceMethodCache.put(method, result); } } return result; } ... }
这个方法中
代码1
首先从serviceMethodCache这个map中找method对应的ServiceMethod,如果没有就走代码2
新建一个,然后通过key = method,value = ServiceMethod的方式将ServiceMethod存入map中。为啥要这样干,很显然,这是为了性能,ServiceMethod.parseAnnotations方法中存在了大量的反射,反射本身是很耗性能的,我们的ServiceMethod可谓得之不易,非常珍贵,这么珍贵的资源当然得存起来,万一下次再用,我就不用重新再次拼命反射获得了。再深入一下,看看ServiceMethod.parseAnnotations究竟是干嘛的abstract class ServiceMethod<T> { static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); ... return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); } ... }
从上述代码看出这里会做两件事情
RequestFactory.parseAnnotations(retrofit, method);
和return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
,接下来我们展开讲讲这两个方法
RequestFactory.parseAnnotations(retrofit, method)方法究竟做了什么
看下面的代码
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
...
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {//代码1
parseMethodAnnotation(annotation);//代码3
}
...
int parameterCount = parameterAnnotationsArray.length;//代码4
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
...
return new RequestFactory(this);//代码6
}
...
}
...
static final class Builder {
...
final Annotation[] methodAnnotations;
final Annotation[][] parameterAnnotationsArray;
...
Builder(Retrofit retrofit, Method method) {
...
this.methodAnnotations = method.getAnnotations();//代码2:获取方法上的注解
this.parameterAnnotationsArray = method.getParameterAnnotations();//代码5:获取方法中参数的注解
...
}
}
调用了parseAnnotations方法后,会直接调用build()方法,这个build方法首先会执行代码1
这个循环的作用就是取出方法上的所有注解以及内容(代码2
中展示了怎么获取方法上的注解),然后进行一个url的字符拼接和确定是post/get/put/...的请求。具体的方法就在代码3
中,我们跟进去看看
private void parseMethodAnnotation(Annotation annotation) {
//设置是什么类型的请求
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);//代码4
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
}
...
//设置头部
else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
}
...
}
其中代码4
中的第二个参数取的就是url后面的东西,比如我的样例中请求方法的头部是这样的@GET("/repos/{owner}/{repo}/contributors") 那取的就是括号中的字符,具体怎么解析的,我们看代码
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError(
method,
"Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod,
httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
// Get the relative URL path and existing query string, if present.
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
String queryParams = value.substring(question + 1);
//这里是大括号的正则
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
this.relativeUrl = value;
//这里是将所有的大括号中的内容根据正则取出赋值给relativeUrlParamNames
this.relativeUrlParamNames = parsePathParameters(value);
}
简单来说,就是保存当前是什么类型的请求,然后请求中的?后面的内容取出来,还有就是替换掉{XXX}中包含的东西并保存好。
代码4开始,就是获取方法中参数的注解并保存到ParameterHandler<?>[] parameterHandlers中,我们可以看到,在代码5中实际上是用了二维数组来保存方法中参数的注解的,这是因为,方法中的参数注解可以有多个比如这个样子
Call<List<Contributor>> contributors(@Field ("ss")@Path("owner") String owner)
所以parameterAnnotationsArray的含义是parameterAnnotationsArray[参数][注解]
,参数可以有很多个,每个参数中对应的注解也可以有很多个
最后执行代码6
返回,接下来分析HttpServiceMethod.parseAnnotations
HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)方法究竟做了什么
总体来说这个方法就是做了两件事
- 选择合适此次请求的CallAdapter
- 选择合适此次请求的Converter
为什么是“合适此次请求”?很简单,我们构建Retrofit的时候肯定可以这样干
new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactoryB.create())
.addCallAdapterFactory(RxJava2CallAdapterFactoryC.create())
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(GsonConverterFactoryB.create())
.addConverterFactory(GsonConverterFactoryC.create())
那你搞出这么多的CallAdapter和Converter的工厂,真正在请求的时候,Retrofit肯定只会用到一个啊,那不得通过返回值来筛选一下子啊,听懂掌声——暗示点赞
具体我们跟进代码中看看
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {//代码7
...
} else {
adapterType = method.getGenericReturnType();//代码8
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);//代码10
Type responseType = callAdapter.responseType();
...
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);//代码11
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);//代码9
} else if (continuationWantsResponse) {
...
} else {
...
}
}
首先要明确一点,HttpServiceMethod是继承ServiceMethod的,然后我们往里面看看。
首先看到代码7
这是一个判断是否为kotlin的Suspend关键字的挂起方法,显然不是,所以执行到了代码8
这个代码的意思是获取请求网络方法的返回值,想想也对,我们要确认是哪个CallAdapter或者Convert那不得从返回值的类型入手嘛。
接下来执行到了代码10
,这段代码就是帮我们选择合适的CallAdapter的,究竟是怎么筛选的呢,来来来我们深入了解下
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
...
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
//noinspection unchecked
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);//代码12
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
...
}
public final class Retrofit {
...
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);//代码13
}
public CallAdapter<?, ?> nextCallAdapter(
@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {//代码15
...
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);//代码14
if (adapter != null) {
return adapter;
}
}
...
}
如上述代码,首先我们的代码10
执行后会有一系列的跳转:代码10
->代码12
->代码13
->代码15
,直接看代码15
,代码15
中有一个skipPast的参数是告诉程序,你不用给我找从第一个CallAdapter.Factory到skipPast之间的所有CallAdapter.Factory了,你给我直接从skipPast后面开始for循环。显然代码13
传的是null,所以暂时不用管,想要继续扩展的可以看下SkipCallbackExecutor
这个注解。
代码15
以后是一个for循环,这个循环的作用是找出符合条件的CallAdapter
,其中最主要的代码是代码14
,如果循环过程中代码14
返回的不为null,就说明已经找到合适的了,并直接返回结果,我们进去代码14
的get(returnType, annotations, this)
方法中一探究竟
abstract class Factory {
/**
* Returns a call adapter for interface methods that return {@code returnType}, or null if it
* cannot be handled by this factory.
*/
public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);//代码16
...
}
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
...
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor executor =//代码17
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {//代码18
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
...
}
执行到代码16
的时候就会代码14
中对应取到的CallAdapter.Factory
来执行get()方法,就拿DefaultCallAdapterFactory来举个例子,代码17
中其实就是判断当前是否需要跳过,如果是跳过就给Executor = null如果不要跳过那就给他原来赋值的callbackExecutor,这个callbackExecutor之前讲过,安卓平台默认就是切换线程用的,代码18
中就返回了一个CallAdapter
,到此代码10
就这样分析完毕
接下来我们看看代码11
,这段代码其实就是帮我们筛选出符合条件的Converter的,老规矩,进去继续深入聊聊
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
...
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);//代码19
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType);
}
}
...
}
public final class Retrofit {
...
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);//代码20
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(//代码23
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
Objects.requireNonNull(type, "type == null");
Objects.requireNonNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;//代码21
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);//代码22
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
...
}
}
上述代码可以看到,过滤Converter和过滤CallAdapter的套路大同小异,我们看看代码流向:代码19
->代码20
->代码23
前面几个代码流向就不多说了,直接看代码21
,这里也是过滤掉了带有SkipCallbackExecutor
的Converter,然后不断循环之前配的converterFactories
,看代码22
循环体中会调用responseBodyConverter()方法,如果返回不为null就表示找到了合适的Converter,进入responseBodyConverter方法康康
abstract class Factory {
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
}
返回null,很明显是通过子类来实现的,以GsonConverterFactory为例
public final class GsonConverterFactory extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
...
}
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
看上述代码其实就是把返回的值转化为json格式,这边就贴两段代码,具体也没啥好讲的,实在有不明白的可以在评论区留个言。
最后我们再看一眼代码9
,这段代码把刚才筛选出的CallAdater和Converter作为参数给了CallAdapted
的构造函数生成了一个新的CallAdapted实体,注意:是CallAdapted 不是CallAdater,CallAdated是最终形态,他是继承HttpServiceMethod的。
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT>
到这里为止所有的东西已经就绪,现在就差调用okhttp,接下来会详细讲解Retrofit是如何调用okhttp的
生成Call
我们的样例代码中有这么一行
Call<List<Contributor>> call = github.contributors("square", "retrofit");
那这个call是怎么来的?嘿嘿,不要忘了,我们分析到现在还只是分析了动态代理中invoke方法中的loadServiceMethod(method)
返回值是怎么来的,那返回值后面还低调地写了一个方法:.invoke(args)
,低调但是也不平凡,这个方法直接返回了Call,且听我娓娓道来。
老规矩,跟代码
abstract class ServiceMethod<T> {
...
abstract @Nullable T invoke(Object[] args);//代码24
}
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
...
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);//代码25
}
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);//代码26
...
}
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
...
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);//代码27
}
}
public interface CallAdapter<R, T> {
...
T adapt(Call<R> call);//代码28
...
}
代码流向是这样:代码24
->代码25
->代码26
->代码27
->代码28
,到代码28
是不是发现走不下去了?那就对了,你看看28是在哪个类下?CallAdapter,是不是很熟悉?没错这个就是之前被我们循环筛选,选出来的工厂造出来地CallAdapter,你回去看一眼代码18
,代码27执行后必然会执行下面的代码(代码18
匿名类中的方法)
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
那么这个executor是什么?当然是Android(extends Platform)平台的类中的MainThreadExecutor啦,当然不可能为null啦,所以肯定会是新建一个ExecutorCallbackCall实例,下面是这个类的代码
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
delegate.enqueue(
new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(
() -> {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on
// cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
@Override
public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
}
});
}
@Override
public boolean isExecuted() {
return delegate.isExecuted();
}
@Override
public Response<T> execute() throws IOException {//代码29
return delegate.execute();
}
@Override
public void cancel() {
delegate.cancel();
}
@Override
public boolean isCanceled() {
return delegate.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override
public Call<T> clone() {
return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
}
@Override
public Request request() {
return delegate.request();
}
@Override
public Timeout timeout() {
return delegate.timeout();
}
}
如果你曾经使用过okhttp,可以在这里看到,几个熟悉的方法enqueue,execute,timeout...我的天,retrofit居然把okhttp中的Call又自己做了一层封装:retrofit2.Call !!!
Retrofit:okhttp老兄,有人说你不好用,连个线程切换都没有,还要人去哼哧哼哧地封装,你早晚让人给毙了
okhttp(惊恐脸):那怎么办,老弟你可得给我支支招啊
Retrofit:也不是不行,你跟我合作,我可是出了名地好用,到时候把你封装进来,用的人就多了啊
okhttp(长叹一口气):多谢老弟,老弟费心,来根烟
okhttp弯下腰递了一根烟给Retrofit
Retrofit(阴险内心独白):我调你用我自己封装的Call,让程序员用我的时候完全感知不到你的存在,我看几年后谁还记得你,咩,哈哈哈哈~
这究竟是道德的沦陷还是。。。
其实是因为Retrofit设计初衷就是想灵活适配网络请求框架的,这样做的话可以体现出我是独立的,你们任何人都可以和我合作,只要做很少的适配。
只不过目前为止还是只适配了okhttp。。。
现在Call已经生成了,我们接着样例代码往下走
List<Contributor> contributors = call.execute().body();
执行网络请求
当执行了call.execute()后就直接执行到了代码29
,在这个方法中发现它执行的是delegate.execute();我们猜测,这个delegate其实就是okhttp的call,证据如下
首先我们再回到梦开始的地方——代理执行invoke方法的地方见代码30
进入invoke方法,我们再看这里面有个啥
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);//代码31
return adapt(call, args);
}
所有的请求都会走这个invoke。我们看看代码31
,这段代码返回的东西,就是我们之前分析的retrofit2.Call,如果能证明OkHttpCall里面的execute方法确实调用了Okhttp3.Call.execute()方法,那整个流程就通了。看代码
final class OkHttpCall<T> implements Call<T> {
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;//代码32
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = getRawCall();//代码33
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
...
@GuardedBy("this")
private okhttp3.Call getRawCall() throws IOException {//代码34
okhttp3.Call call = rawCall;//代码35
if (call != null) return call;
...
// Create and remember either the success or the failure.
try {
return rawCall = createRawCall();//代码36
} catch (RuntimeException | Error | IOException e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
...
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));//代码37
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
}
代码流向:代码33
->代码34
->代码35
->代码36
->代码37
,还记得callFactory的赋值吗?对的,就是如果在build构建Retrofit时没有赋值,那就默认OkhttpClient,下面的代码帮助回忆一下:
public Retrofit build() {
...
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
...
}
证毕,结果:实锤retrofit2.Call == okhttp3.Call
接下来执行execute方法其实就是okhttp3.Call.execute(),我之前写了一篇文章是以okhttp3.Call.equeue()为例的,其实原理都差不多,有兴趣的可以看一下我的文章:okhttp3原理解析——概览(非常概括)
结语
头一回写这么长篇的源码解析,写到这里其实也涨了不少的知识,阅读源码确实是一个很好的习惯,都说阅读书本是在和圣贤交流,交流多了就能成为圣贤。那阅读优秀源码其实也是在和大神交流,交流多了不说能成为大神,至少对以后的学习和能力的提升都是一笔不小的财富。Retrofit确实是一个很优秀的开源框架,我也是看了3天才大致了解里面的工作流程,好的东西真的是每次看都能发现新的知识,每次都能将自己的认知升那么一小段。