概述
在最近的项目中,由于对于Retrofit仅仅处于用的状态,导致很多代码虽然实现了功能,但是性能和书写上出现了很大问题。在项目上线之后,现在终于有时间可以深入一些源码,通过看源码了解Retrofit的运行方式,文中基本上都是个人的想法,如果有错误,请指出。
对于需要理解的Retrofit源码,大致需要知道如下几个类:convertFactory,callAdapterFactory,ServiceMethod,OkHttpClient,OkHttpCall,它们的关系基本上可以用下面一张图概述:
这里面很多新的类型的面孔,那么在源码分析中再慢慢说明吧。而对于convertFactory和callAdapterFactory的作用,基本上可以按照下图描述:
可以理解为convertFactory是决定接口返回值的泛型参数,也是http请求的数据最终需要转化的数据类型,而adapterFactory决定了接口的返回值。 这么说的,可能大家还是有些不太明白,那么我们还是一步一步看源码吧。
基本请求方式
对于大多数使用Retrofit的网络请求,基本上的采取相似的套路,那就是Retrofit封装 + 定义请求接口 + 网络请求。那么我们也这么做一次吧。
Retrofit封装
基本封装如下:
public class RetrofitUtils {
private static Retrofit retrofit ;
public static <T> T createService(Class<T> clazz) {
if (retrofit == null) {
synchronized (RetrofitUtils.class) {
Retrofit.Builder builder = new Retrofit.Builder();
retrofit = builder.baseUrl(Api.BASE_IP)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
}
return retrofit.create(clazz);
}
}
定义请求接口
我们一般定义的请求接口如下,大致的相似套路也是相似的:
public interface Services {
@GET("/")
Call<String> getBaiduInfo() ;
@GET("/userinfo/{uid}")
Class<User> getUserInfo(@Path("uid") String uid , @Query("token") String token);
@GET("/all/bookinfo")
Observable<List<Book>> getAllBookInfo(@Query("page") int page);
}
简单应用
写个简单的请求小例子,比如我们使用第一个接口获取baidu首页的信息:
new Thread(){
@Override
public void run() {
final Call<String> info = RetrofitUtils.createService(Services.class).getBaiduInfo();
info.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, final Response<String> response) {
Log.d("response",response.body());
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Log.d("tag failure " , t.toString());
}
});
}
}.start();
所做的操作基本上就是这种模式,开发中无非是转变成Observable,然后使用Rx操作符实现各种变换,思路大致是一样的。
源码分析
这次的分析,是根据Retrofit最新源码(17/08/12),版本2.3分析的,贴的代码基本上都是经过精简的代码,去掉了一些不太重要的代码,比如异常处理,空检查等等。如果大家想看实现过程,不妨去下载一下最新的代码尝试阅读。
Retrofit
下面来分析一下Retrofit到底是怎么运行的。在RetrofitUtils代码中,可以很看出它是由经典的Builder模式构建的:
builder.baseUrl(Api.BASE_IP)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
Retrofit.Builder内部分别使用了两个List保存了convertFactory和adapterFactory,那么我们此时记录一下,converterFactories中现在存在ScalarsConverterFactory和GsonConvertFactory两个factory;adapterFactories现在存在一个RxJavaCallAdapterFactory,然后执行Builder.build方法。这个很重要,大家先打个tag。
这里这个Builder.build需要说明一下,因为它默认了一些东西,有些值得我们重视,build源码精简如下:
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
这里有三点需要注意一下
第一: 这里的callFactory我们没有赋值,因此它就是OkHttpClient(),这个先记住;
第二: 这里的callbackExecutor 我们也没有赋值,因此它也使用默认的callbackExector,而默认的platfrom.defaultCallbackExectuor(),也就是我们的大Android的Handler(Looper.mainLooper()); 这一点大家可以查看一下源码,因为简单我就贴出来了;
第三: adapterFactories除了添加了RxJavaCallAdapterFactory.create()之外,还默认添加了一个platform.defaultCallAdapterFactory(callbackExecutor). 这个先记住就好。
以上三点先做个小笔记,因为后面的解析中也会使用到它们的。
好了,有了这些参数之后,我们就可以构建了new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly). 那么Retorfit的初始化就先告一段落了。
create(final Class<T> clazz)
这可以说是Retrofit中最重要的一个方法了,方法的量很小,只是使用了动态代理:
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() {});
对于动态代理,这里我就不扯了,涉及到的东西太多了,而我也是个半吊子,我们这里只需要知道它可以返回一个clazz的代理实例就行了. 然后使用这个clazz的实例,调用它的方法,就要使用InvocationHandler#invoke()方法了,这个方法的意义是在代理实例上处理方法并返回相应的结果。本例中invoke方法精简如下:
@Override
public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
光说理论可能有些找不到北,那么就说说例子吧。 就拿我们的Services接口来说,它存在三个方法:getBaiduInfo,getUserInfo,getAllBookInfo.对于java来说,万物皆为对象,那么这个三个方法在Class类的基础上就是三个Method对象。这里先记一笔,等会还要回来。
在invoke方法中,我们需要认识到一个非常重要的类:ServiceMethod. Retrofit中有个缓存ServiceMethod的Map,这里loadServiceMethod()方法就是通过Method获取ServiceMethod,如果没有则重新创建新的ServiceMethod。
ServiceMethod
现在我们去看Retrofit.create()方法中动态代理的invoke()中有个loadServiceMethod()方法,进去看一下:
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
这里的serviceMethodCache是Map型对象,是一种很典型的缓存思想,这里就不多说了,主要看new ServiceMethod.Builder<>(this,method).build()到底干了什么。可以清楚看到,ServiceMethod也是采用了Builder模式,源码如下:
public ServiceMethod build() {
//这里获取合适的callAdapter
callAdapter = createCallAdapter();
//获取方法返回值的参数类型
responseType = callAdapter.responseType();
//这里获取合适的ConvertAdapter
responseConverter = createResponseConverter();
//解析注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
//判断注解的合法性 主要和Http请求中设置有关 比如是否有请求方法 有没有请求体等参数
//代码省略
return new ServiceMethod<>(this);
}
就如同图2中所解释的,callAdapter 和responseConverter 将伴随Retrofit的整个生命周期。这个ServiceMethod通过要使用的代理方法,通过方法的返回值和返回值参数,分别获取相应的合适的callAdapter和responseConverter。那么我们分别来看一下,这些callAdapter和responseConverter到底是怎么获取的。先看createCallAdapter():
private CallAdapter<T, R> createCallAdapter() {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
}
这里的method.getGenericReturnType()是什么意思呢? 举个例子啊:
public Map<String,Double> getMap() {
return null;
}
这个getMap(),最为Method,它的getGenericReturnType结果是:
java.util.Map<java.lang.String, java.lang.Double>
也就是我们方法的返回值,而且还带有参数。这个比较高大上啊。这个先理解一下,因为下面会用得到的。
而method.getAnnotations()是返回方法头上的注解,什么@GET,@POST就可以通过这个方法进行获取的。
知道了两个参数的意思,那我们来看看retrofit.callAdapter(returnType,annotations)是怎么操作的吧,在Retrofit它最终会调用nextCallAdapter()方法,具体精简代码如下:
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,Annotation[] annotations) {
int start = 0;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
throw RuntimException("not find the correct callAdapter");
}
需要从adapterFactories的List集合中拿数据进行判断,那我们需要看看adapterFactories.get(i).get(returnType,annotations,this)方法了。通过对Retrofit.Builder()的分析,我们知道现在adapterFactories中有两个adapter:RxJavaCallAdapterFactory.create() 和 platform.defaultCallAdapterFactory(callbackExecutor). 那我们先看一下RxJavaCallAdapterFactory.create()中
get(returnType,annotations,retrofit)返回的是什么吧。
为了简单,我们Services中的一个最简单的方法: Call<String> getBaiduInfo(),它的返回值为Call<String> 去看看adapterFactories中的get方法吧:
现在我们需要遍历adapterFactories种的Adapter,对于RxJavaCallAdapterFactory.create():,其中get()的源码为:
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
Class<?> rawType = getRawType(returnType);
boolean isSingle = rawType == Single.class;
boolean isCompletable = rawType == Completable.class;
if (rawType != Observable.class && !isSingle && !isCompletable) {
return null;
}
if (isCompletable) {
return new RxJavaCallAdapter(Void.class, scheduler, isAsync, false, true, false, true);
}
return new RxJavaCallAdapter(responseType, scheduler, isAsync, isResult, isBody, isSingle,
false);
}
这里的getRawType()是调用方法的返回值类型,那么Call<String> getBaiduInfo()的返回值类型就是Call了。那么RxJavaCallAdapterFactory.create().get()返回的是null。那么我们现在只有看一下platform.defaultCallAdapterFactory(callbackExecutor)中get()方法了:
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
由于getRawType(returnType)的返回值是Call.class,对,这就是我们需要CallAdapter了,此时responseType就是Call的参数值String.class,同时还需要注意的是adapt方法,返回的是一个new ExecutorCallbackCall(),这里先记住,回头还需要用到。
我们到现在分析了ServiceMethid.Builder.build()中 createCallAdapter();同时也知道了responseType是String.class类型的,那么我们现在就需要分析 responseConverter = createResponseConverter();方法了。
现在来看一看createResponseConverter()方法吧:
private Converter<ResponseBody, T> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
return retrofit.responseBodyConverter(responseType, annotations);
}
尼玛,又是辗转反侧又要向retrofit要东西,好吧,我们看看吧retrofit.responseBodyConverter,这个方法和callAdapter一样,最终也会调用nextResponseBodyConverter方法,代码如下:
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
int start = 0;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
throw new RuntimeException("cannot find correct converter");
}
同样,还是需要找我converterFactories中查找合适的Converter。此时convertFactory中有ScalarsConverterFactory.create()和GsonConverterFactory.create()两个convertFactory,那么先看ScalarsConverterFactory.create():
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (type == String.class) {
return StringResponseBodyConverter.INSTANCE;
}
if (type == Boolean.class || type == boolean.class) {
return BooleanResponseBodyConverter.INSTANCE;
}
...
//代码省略
return null;
}
此时我们的type是String.class,那我们就需要StringResponseBodyConverter.INSTANC,进去看一下:
static final class StringResponseBodyConverter implements Converter<ResponseBody, String> {
static final StringResponseBodyConverter INSTANCE = new StringResponseBodyConverter();
@Override public String convert(ResponseBody value) throws IOException {
return value.string();
}
}
现在到了这里,我们已经知道了callAdapter和responseConverter的最终值了,关于ServiceMethod.Builder.build()也已经完成了【解析注解大家可以看一下,主要是根据不同的注解走不同的分支】,到现在为止,ServiceMethod的初始化完成。
OkHttpCall
由于ServiceMethod的初始化已经完成,那么现在我们继续回到Retrofit.create()方法中的动态代理中,来看这个行代码:
ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
ServiceMethod我们已经知道是什么了,callAdapter也知道是哪一个了[不知道的往回看,的确是比较卡,因为东西的确是蛮多的]。现在不急于分析上述代码中OkHttpCall是什么东西,咋们先看一下我们的网络请求时怎么写的:
final Call<String> info = RetrofitUtils.createService(Services.class).getAllInfos();
info.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, final Response<String> response) {
Log.d("response",response.body());
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Log.d("tag failure " , t.toString());
}
});
我们现在已经知道了RetrofitUtils.createService(Services.class).getAllInfos()是ExecutorCallbackCall(来自platform.defaultCallAdapterFactory),就是serviceMethod.callAdapter.adapt()的返回值:
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
而这个ExecutorCallbackCall的构造函数和enqueue方法为:
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
public void enqueue(final Callback<T> callback) {
delegate.enqueue(new Callback<T>() {
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
public void run() {
if (delegate.isCanceled()) {
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
最终我们发现,网络请求是有delegate代理的,而这个delegate是从哪里来的呢?对了,是从serviceMethod.callAdapter.adapt()的参数中传递过来的,那么这个delegate就是OkHttpCall:
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
那么我们去看一下OkHttpCall的enqueue方法吧:
@Override
public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
Throwable failure;
synchronized (this) {
//拼接Ok3的call请求
call = rawCall = createRawCall();
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response = parseResponse(rawResponse);
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callback.onFailure(OkHttpCall.this, e);
}
private void callFailure(Throwable e) {
callback.onFailure(OkHttpCall.this, e);
}
private void callSuccess(Response<T> response) {
callback.onResponse(OkHttpCall.this, response);
}
});
}
这里有几点需要主要的是:
1.所有的请求都只能进行一次,多次进行就会报错;
2.因为Retrofit并未进行真正的网络请求,而是将网络请求外包给了OkHttp3,所以这里面有很多类型的概念,比如说Call,有Retrofit的call,也有Ok3的call,不过还好,Ok3的call都给出了前缀。
我们来看一下拼接ok3的createRawCall()方法:
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
return call;
}
它主要做了两件事,使用serviceMethod构建了网络请求的request,还记得我们使用的@GET,@POST,@PATH,@Header,url等网络基本请求都在serviceMethod中解析的吧,这个toRequest就是将各种参数组装,然后变成了Request参数,具体的细节我就不说了,大家有兴趣可以直接去看源码。
而serviceMethod.callFactory.newCall(request)是什么呢? 我们一步一步来看,serviceMethod.callFactory是来自Retorfit的callFactory,来看Retrofit的callFactory的构建过程:
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
是的,我们的callFactory并没有被初始化,因此它就是OkHttpClient(),对的,就是它。那我们就看看OkHttpClient.newCall(request)方法吧:
@Override
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
那么这个call其实真正起作用的就是RealCall类,回到createRawCall()方法中来,那么将由RealCall执行enqueue方法:
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
看到client.dispatcher().enqueue(new AsyncCall(responseCallback)),我们可以理解为它在进行网络请求了,这里涉及到了OKHttp3的知识了,具体的内容,可以参考一下这篇文章。这里就不多讲了,到这里来,我们就认为是ok3给我们去进行网络请求了。
回到OkHttpCall的enqueue的方法,请求网络请求之后,结果以okhttp3.Response返回,那我们来看一下:
Response<T> response = parseResponse(rawResponse);
来看一下源码:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
//各种responseCode != 200的情况 我们不考虑这么多 只考虑完全正常的情况
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
}
忽略次要的代码,看一下这行:
T body = serviceMethod.toResponse(catchingBody);
而ServiceMethod的toResponse方法为:
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
而responseCOnverter是我们已经定义好的StringResponseBodyConverter.INSTANC,那么它的convert方法是:
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
直接将我们的ResponseBody对象转化为了String对象。而这个Body最后作为参数进入到了Response.success(body,rawResponse)中,最后被回调到了Callback中。
好了,一次最基本的Retrofit网络请求就这样完成了,不得不说Retrofit设计得比较精巧,初次读起来还是比较难懂,这篇文章前前后后写了6,7个小时,就是因为有的东西忘了就给噎住了,又得重新再来一次。
最后还是来总结一遍内容:Retrofit通过Builder模式构建,通过动态代理方法,在ServiceMethod的帮助下,解析了方法注解,确定了请求方法的返回值和请求的结果最终转化的对象。这个框架本身需要一些Java的高阶知识,比如动态代理,反射方法还有各种接口组合,不过分析一遍之后,还是还是蛮清晰的,值得我们去学习一下,毕竟这货现在github上android类请求上高居榜首,真是不无道理的。
好了,今天的分析就到这里了,文章中可能有些理解不到位的,希望大家啊指出,共同进步。