今天我来记录一下我对Retrofit框架的理解。不得不说,Retrofit的架构设计极其优秀,既保证了使用上的方便,又高度解耦。同时Retrofit又是学习设计模式的不二典范,里面使用的设计模式数不胜数。今天我们来详细的剖析这个框架。
本文参考资料:
1. 这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解 )
2. Android:手把手带你深入剖析 Retrofit 2.0 源码
由于Retrofit里面涉及到RxJava和Gson相关知识,所以在阅读本文之前,最好是对RxJava原理有一定的了解。至于Gson,只要会用就行,其实我也不太了解Gson的原理?。
1. 先逼逼几句
众所周知,Retrofit是基于OkHttp设计的一个网络请求框架。其实说Retrofit是一个网络不是那么准确,准确来说,Retrofit就是OkHttp的一个外壳,Retrofit的底层网络请求使用就是OkHttp。
同时,Retrofit完全兼容RxJava,这使得它的使用范围进一步扩大,因为这个特性,进一步促进演变近几年比较火的Android架构框架 - RxJava + Retrofit + MVP
。
本文打算从Retrofit的基本使用开始,深入分析Retrofit的整个工作流程,包括线程调度、与RxJava兼容等等。
2. Retrofit组成部分
Retrofit分为4个部分,我们来看看。
名字 | 含义 |
---|---|
自定义的请求接口 | 主要定义一些网络请求基本参数,比如请求方式(get或者post),表单等等 |
由请求接口生成OkHttp的Request | 根据自定义的请求接口,生成对应的可以执行的Request ,此过程只要在ServiceMethod 里面完成 |
网络请求执行器-Call | 用来执行生成的Request ,在Retrofit里面,使用了装饰者模式,OkHttpCall 只是外壳,内部的Call 才是真正执行Request 的 |
数据转换器-Converter | 主要是将返回的Response 解析成为我们想要的Java类对象 |
提一句,Retrofit是适合于Restful API的网络请求框架,如果想要让Retrofit支持非Restful API,可以参考这一篇文章:如何使用Retrofit请求非Restful API(悄悄的给你们说一句,这个问题,我在腾讯面试被问过?)
我们先大体看看Retrofit怎么通过这四个部分来进行工作。

3. 基本使用
本文简单介绍一下Retrofit的基本使用,让大家有一个印象。但是毕竟不是初级文章,不会在方面花很多的时间。
首先需要定义一个接口。
public interface Service { @POST("getSongPoetry") @FormUrlEncoded Observable<Bean> getCall(@Field("page") int page, @Field("count") int count);}
然后创建一个Retrofit
的对象。
private Retrofit mRetrofit = new Retrofit.Builder() .baseUrl("https://api.apiopen.top/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();
然后通过Retrofit
的对象获得接口的代理对象。
Service service = mRetrofit.create(Service.class);
最后调用对应的接口来请求数据。
service.getCall(1, 20) .observeOn(Schedulers.newThread()) .subscribeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Bean>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Bean bean) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
整个Retrofit的网络请求过程就是这样,是不是非常的简单?这里面我们根本不需要考虑线程调度的问题,以及数据解析的问题,Retrofit已经给我们做了。
4. Retrofit
从现在开始,我们正式开始分析Retrofit整个工作流程。我们先来看看Retrofit对象的创建。
private Retrofit mRetrofit = new Retrofit.Builder() .baseUrl("https://api.apiopen.top/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();
从这里,我们已经可以看到两个设计模式--建造者模式
和工厂模式
。当然我们也没必要紧张,就是两个的设计模式而已。
这里通过调用Retrofit
一个内部类Builder
一系列的方法,进而初始化了Retrofit
一些必须的对象。
(1).Builder
我们先来看看Builder
类,我们先从它的成员变量开始
private final Platform platform; private @Nullable okhttp3.Call.Factory callFactory; private HttpUrl baseUrl; private final List<Converter.Factory> converterFactories = new ArrayList<>(); private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(); private @Nullable Executor callbackExecutor; private boolean validateEagerly;
变量名 | 类型 | 作用 |
---|---|---|
platform | Platform | 平台的对象,通过调用Platform 的get 方法,可以获得适合当前环境的平台,包括Java8、Android(似乎Retrofit 2.4.0将其他的平台移除了)。Platform 可以通过调用defaultCallAdapterFactory 方法可以获得网络执行适配器的工厂类,调用defaultCallbackExecutor 方法可以获得线程调度的对象 |
callFactory | okhttp3.Call.Factory | 网络请求执行器的工厂类,用来创建网络请求执行器的对象 |
baseUrl | HttpUrl | 网络请求的URL基地址,完整的URL地址应当是baseUrl + relativeUrl 。其中,调用Builder 的baseUrl 方法设置的就是基地址,在接口中@Post 注解里面的值就是relativeUrl |
converterFactories | List<Converter.Factory> | 数据转换器的数组,当收到Response时,Retrofit会根据接口里面定义的返回类型在converterFactories 里面找到合适的数据转换器,从而将Response转换成为正确的类型。 |
callAdapterFactories | List<CallAdapter.Factory> | 网络请求执行适配器的数组,跟List<CallAdapter.Factory> 一样,会根据接口里面定义的类型来找到合适的适配器。但是二者有所不同,待会会详细的分析。 |
callbackExecutor | Executor | 回调线程池,主要是将结果发送到指定的线程,比如,在Android平台上,肯定是将结果发送到主线程。 |
validateEagerly | boolean | 是否提前将接口里面的方法解析成为ServiceMethod 。至于什么是ServiceMethod ,后面会详细的解释。 |
上表中详细的解释了Builder
每一个成员变量的作用,在Builder
类中,每一个成员变量都有一个对应的方法用来赋值。比如说addCallAdapterFactory
方法可以往callAdapterFactories
添加一个适配器,其他的也是如此。
接下来我们看看Builder
的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> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>(1 + this.converterFactories.size()); // 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); return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); }
哟哟,这个build
方法里面还做了不少的事情的,我们来看重点。
if (callFactory == null) { callFactory = new OkHttpClient(); }
从这里,我们可以看出来,如果我们设置网络请求执行器,默认就是OkHttpClient
,这个OkHttpClient
在OkHttp
是一个非常核心的类,是网络请求必须的类。这里不对OkHttpClient
进行展开,毕竟是OKHttp
的相关知识。
if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); }
同理,如果回调线程池默认也为该平台的回调线程池。比如说,如果是Android平台,那么肯定是调用这段代码:
@Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); }
从MainThreadExecutor
的名字,我们就可以得出结果,肯定是用来回调到主线程的,毕竟在Android中,只有主线程才能更新UI?。
至于其他的,都是常规的赋值,这里就不详细讲解了。
在build
方法最后一行代码,创建了一个Retrofit
的对象。我们来看看Retrofit
的构造方法。
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl, List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories, @Nullable Executor callbackExecutor, boolean validateEagerly) { this.callFactory = callFactory; this.baseUrl = baseUrl; this.converterFactories = converterFactories; // Copy+unmodifiable at call site. this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site. this.callbackExecutor = callbackExecutor; this.validateEagerly = validateEagerly; }
其实也没做什么事情,就是将Builder
的成员变量对应的赋值给Retrofit
。
(2).Retrofit的创建为什么要使用建造者模式?
我们发现,经过一些操作,最终的目的就是创建一个Retrofit
对象,然后给Retrofit
的成员变量赋值,为什么我们需要多一个Builder
,然后绕一大圈来创建一个Retrofit
对象呢?
我首先问一句,在外面写这么一句代码就能创建完整的Retrofit
对象,觉得爽不爽?
private Retrofit mRetrofit = new Retrofit.Builder() .baseUrl("https://api.apiopen.top/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();
不用说,肯定爽,首先外部代码简洁,通过调用对应方法来给变量赋值,链式编程方格6的不行。其次,在Retrofit
的构造方法也是非常的简洁,没有各种判断,所有的工作都在Builder
的build
方法做了。
所以,我们从Retrofit
里面得到,如果一个对象得创建需要给很多的成员变量,不妨使用建造者模式,这使得你的代码更加的简洁。外部使用起来也是非常的简单。
(3).create方法
从上面的栗子,我们可以知道,当创建完毕一个Retrofit的对象时,如果想要进行网络请求的话,还必须创建接口的代理对象,也就是通过调用create
方法来获得一个对象。
从我的言语之间,你们应该都知道,create
方法肯定使用了代理模式,要不然何来代理对象
之称。我们来看看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, @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); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.adapt(okHttpCall); } }); }
哎呀哎呀,懵逼还是懵逼,没错!对于那些Java功底还不够牢固的同学来说,这个方法单单是看一眼就很懵逼,当初我看这个方法时,也是一脸懵逼。
不过,懵逼归懵逼。我想说的是,这段代码是整个Retrofit的画龙点睛之笔,特别是其中的三行代码,都可以称之为神来之笔。
我们先来粗略的看看这段代码。
Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); }
首先是validateEagerly
相关的。第一行代码是验证当前这个Class对象是否合法。至于是否合法说的太笼统了,具体就是判断当前的Class是否是一个接口,并且没有继承接口。总之,就是验证Class是否一个单纯的接口。
然后就是eagerlyValidateMethods
方法的调用,如果validateEagerly
为true的话,那么就调用eagerlyValidateMethods
方法。至于eagerlyValidateMethods
方法里面干了什么,在这里,先不急着说,等后面咱们分析ServiceMethod
类时,咱们再来好好的说道说道?。在这里先提一句,eagerlyValidateMethods
方法就是将接口的里面的方法解析成为ServiceMethod
对象,这个也是validateEagerly
变量的作用,之前也说过。
看完了简单的几行代码,接下来的代码就是让人彻底懵逼的代码。说到底,其实也没啥,这里就是一个JDK的动态代理,至于不懂JDK代理的同学赶快去学习吧,这个非常的重要。
闲话少扯,我们来看看invoke
方法。前面的几个判断都没有用,在Android平台上根本不会调用,我们直接来看看最后三行代码:
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.adapt(okHttpCall);
这三行代码,可以被整个Retrofit最核心的代码。表面上来看,只有三行代码,实际上背后给我们做的事情那是数不胜数的。我们来一行一行的看。
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
这行代码的目的是将一个普通的Method
转换成为ServiceMethod
对象。至于怎么转换的话,还有ServiceMethod
是什么,这里先不急着说,我只想说ServiceMethod
是Retrofit
里面非常非常核心的一个类。
我们继续来看第二行代码:
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
这里比较好理解,就是将ServiceMethod
对象包装成为一个OkHttpCall
对象,这个OkHttpCall
对象使用来执行网络请求的。其实它使用了装饰者模式,本身就是一个壳,这个在后面会详细解释。
最后,我们来看看最后一行代码:
return serviceMethod.adapt(okHttpCall);
这里调用了ServiceMethod
的adapt
方法。这里我们可以看看干了吗。
T adapt(Call<R> call) { return callAdapter.adapt(call); }
好吧,又调用了callAdapter
的adapt
方法。至于adapt
方法到底干了什么,这不进行展开,后面再讲解CallAdapter
的Factory
时,会详细说到。这里,我们可以这么认为,就是通过调用了callAdapter.adapt
,如果此时的CallAdapter
是RxJava相关的Adapter
,那么这里返回就是Observable<>
之类的。
总之,最后一行代码,就是使用适配器用来适配的。
5. ServiceMethod
现在我们来分析一下这个核心之核心的类。我们还是先从成员变量开始。
static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*"; static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}"); static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM); private final okhttp3.Call.Factory callFactory; private final CallAdapter<R, T> callAdapter; private final HttpUrl baseUrl; private final Converter<ResponseBody, R> responseConverter; // -------------------- //网络请求相关参数 private final String httpMethod; private final String relativeUrl; private final Headers headers; private final MediaType contentType; private final boolean hasBody; private final boolean isFormEncoded; private final boolean isMultipart; // -------------------- private final ParameterHandler<?>[] parameterHandlers;
哎呀,这么多我们挑几个重要分析。感觉没什么要分析,callFactory
、callAdapter
、responseConverter
这些在都是Retrofit
里面的含义都是一样的,只不过Retrofit
里面是数组,这里是单个变量,表示从数组选出来的合适的,待会会详细的分析怎么选择。
唯一值得分析的就是parameterHandlers
变量,这个变量是由接口方法的每个参数解析而来的。我们看到,在接口方法里面,每一个参数都会带一些注解,这些信息都是由parameterHandlers
变量来保存,并且可以根据每一个参数的特点,生成特定对象供Request
的构建。这些我们在后面都会详细讲解。
现在,我们可以回去Retrofit的eagerlyValidateMethods
方法和loadServiceMethod
方法。
我们知道,当validateEagerly
变量为true时,会调用eagerlyValidateMethods
方法将接口中的每个方法解析成为ServiceMethod
。我们先来看看:
private void eagerlyValidateMethods(Class<?> service) { Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { if (!platform.isDefaultMethod(method)) { loadServiceMethod(method); } } }
eagerlyValidateMethods
方法里面也没有做什么事,具体的操作都在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; }
嗯,代码非常的简单,但是代码简单不代表容易理解。我们来具体的分析分析。
首先是判断缓存里面是否有,如果没有的话,就根据条件创建一个ServiceMethod
对象,我们看到ServiceMethod
对象的创建使用的也是建造者模式。我们先来看看ServiceMethod.Builder
的构造方法:
Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; this.methodAnnotations = method.getAnnotations(); this.parameterTypes = method.getGenericParameterTypes(); this.parameterAnnotationsArray = method.getParameterAnnotations(); }
相信熟悉Java的同学,这几行代码应该难不倒你们吧?前两句我就不说了,我来解释后三句。
this.methodAnnotations = method.getAnnotations();
这行代码是获取当前方法的注解,比如说,我们在上面的getCall方法添加了@POST
注解和@FormUrlEncoded
注解,这里都可以获得。
this.parameterTypes = method.getGenericParameterTypes();
这个是获取每个参数的类型。
this.parameterAnnotationsArray = method.getParameterAnnotations();
这个是获取每个参数的注解,就比如上面的@Field
。
然后,我们再来看看ServiceMethod.Builder
的build
方法。
public ServiceMethod build() { callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); if (responseType == Response.class || responseType == okhttp3.Response.class) { throw methodError("'" + Utils.getRawType(responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?"); } responseConverter = createResponseConverter(); for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } if (httpMethod == null) { throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.)."); } if (!hasBody) { if (isMultipart) { throw methodError( "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); } if (isFormEncoded) { throw methodError("FormUrlEncoded can only be specified on HTTP methods with " + "request body (e.g., @POST)."); } } int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; if (Utils.hasUnresolvableType(parameterType)) { throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType); } Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; if (parameterAnnotations == null) { throw parameterError(p, "No Retrofit annotation found."); } parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } if (relativeUrl == null && !gotUrl) { throw methodError("Missing either @%s URL or @Url parameter.", httpMethod); } if (!isFormEncoded && !isMultipart && !hasBody && gotBody) { throw methodError("Non-body HTTP method cannot contain @Body."); } if (isFormEncoded && !gotField) { throw methodError("Form-encoded method must contain at least one @Field."); } if (isMultipart && !gotPart) { throw methodError("Multipart method must contain at least one @Part."); } return new ServiceMethod<>(this); }
build
方法相对来说比较长,这里我将它分为4步:
1. 调用
createCallAdapter
方法,从Retrofit
里面选择合适当前方法的CallAdapter
。
2. 调用createResponseConverter
方法,从Retrofit
里面选择合适当前方法的Convert
。
3.调用parseMethodAnnotation
方法,解析接口方法的注解配置。比如说,上面我们在getCall
方法设置了@POST
和@FormUrlEncoded
注解,在这一步都会被解析出来。
4. 调用parseParameter
方法将方法的每个参数解析成为ParameterHandler
对象。
我们一步一步的分析。首先来看第一步,也就是createCallAdapter
方法的调用。
private CallAdapter<T, R> createCallAdapter() { Type returnType = method.getGenericReturnType(); if (Utils.hasUnresolvableType(returnType)) { throw methodError( "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw methodError("Service methods cannot return void."); } Annotation[] annotations = method.getAnnotations(); try { //noinspection unchecked return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(e, "Unable to create call adapter for %s", returnType); } }
createCallAdapter
表达的意思非常简单,就是根据方法的返回类型和注解类型去Retrofit
里面去寻找合适的CallAdapter
。这里,我们先来看看Retrofit
的callAdapter
方法到底做了什么。
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); }
callAdapter
方法也是外壳,实际的工作在nextCallAdapter
方法里面做的,我们来看看nextCallAdapter
方法:
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { checkNotNull(returnType, "returnType == null"); checkNotNull(annotations, "annotations == null"); 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); if (adapter != null) { return adapter; } } StringBuilder builder = new StringBuilder("Could not locate call adapter for ") .append(returnType) .append(".\n"); if (skipPast != null) { builder.append(" Skipped:"); for (int i = 0; i < start; i++) { builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName()); } builder.append('\n'); } builder.append(" Tried:"); for (int i = start, count = callAdapterFactories.size(); i < count; i++) { builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName()); } throw new IllegalArgumentException(builder.toString()); }
写了那么多,归根结底就是一句话,去寻找合适的CallAdapter,如果找不到的话,就抛出异常。这里我们需要特别的注意一行代码:
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
这里的意思就是去callAdapterFactories
的每个元素寻找合适的CallAdapter
。注意CallAdapter.Factory
的get
方法,后面我们在分析CallAdapter.Factory
会重点的讲解它。
这里先不对CallAdapter.Factory
展开,后面会详细的分析。
以上就是ServiceMethod.Builder
的build方法第一步,也就是寻找合适CallAdapter
。我们现在再来看看第二步。
第二步是调用createResponseConverter
方法,寻找合适的数据转换器。其实跟第一步差不多,我们来简单看看。
createResponseConverter
方法最终会调用到Retrofit
的nextResponseBodyConverter
方法。在nextResponseBodyConverter
方法里面,我们只需要注意一点:
Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this);
这里跟第一步的很像,同样的道理,后面我们在分析数据转换器时会详细的分析它。这里先有一个印象就行。
第三步就是调用parseMethodAnnotation
方法来解析方法的注解,这里所做目的有两点:
- 获取网络请求的配置参数,用以
Request
的构建。- 验证注解是否合法。比如说,在上面的栗子中,使用POST请求,同时还每个参数还有
@Field
注解,所以该方法的注解必须有@FormUrlEncoded
,否则会抛出异常。这些都是Retrofit
的基本规则而已。
最后,我们来看最后一步。最后一步整个4步中最复杂,代码量最多的一步。我们来详细分析分析。
ServiceMethod
通过调用parseParameter
方法将每个参数解析成为ParameterHandler
对象。
而这个ParameterHandler
对象到底是怎么一个东西,接下来我们会通过parseParameter
方法来详细的分析。
通过parseParameter
方法的源码,我们可以看到parseParameter
方法内部还是调用了parseParameterAnnotation
来完成具体的解析工作。由于parseParameterAnnotation
方法太特么的长了,这里我们拿一个特例来分析,就上面栗子中的@Field
注解来分析:
} else if (annotation instanceof Field) { if (!isFormEncoded) { throw parameterError(p, "@Field parameters can only be used with form encoding."); } Field field = (Field) annotation; String name = field.value(); boolean encoded = field.encoded(); gotField = true; Class<?> rawParameterType = Utils.getRawType(type); if (Iterable.class.isAssignableFrom(rawParameterType)) { if (!(type instanceof ParameterizedType)) { throw parameterError(p, rawParameterType.getSimpleName() + " must include generic type (e.g., " + rawParameterType.getSimpleName() + "<String>)"); } ParameterizedType parameterizedType = (ParameterizedType) type; Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations); return new ParameterHandler.Field<>(name, converter, encoded).iterable(); } else if (rawParameterType.isArray()) { Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations); return new ParameterHandler.Field<>(name, converter, encoded).array(); } else { Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Field<>(name, converter, encoded); }
@Field
注解部分还是那么的多,不过我们不用担心,我们只需要关心最后的三个return部分。为了简单起见,我们先分析最后一个return。
Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Field<>(name, converter, encoded);
其中第一句是去Retrofit
里面去寻找合适的StringConverter
。StringConverter
顾名思义,就是将一个对象转换成为String
字符串。就比如说,上面的栗子,我们写的是int
类型,而在网络请求时,提交表单时,这些数据都是String
类型。这个就是StringConverter
的作用。
最后就是创建ParameterHandler.Field
对象。我们先来看看这个类。
static final class Field<T> extends ParameterHandler<T> { private final String name; private final Converter<T, String> valueConverter; private final boolean encoded; Field(String name, Converter<T, String> valueConverter, boolean encoded) { this.name = checkNotNull(name, "name == null"); this.valueConverter = valueConverter; this.encoded = encoded; } @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException { if (value == null) return; // Skip null values. String fieldValue = valueConverter.convert(value); if (fieldValue == null) return; // Skip null converted values builder.addFormField(name, fieldValue, encoded); } }
我们在ParameterHandler
时,通常来说只需要关注他的apply
,因为后面ServiceMethod
在构建Request
是通过调用ParameterHandler
的apply
方法来将一个参数转换成为Request
需要的数据。
Field
的apply
方法非常简单,这里就不多说了。
而其他两个return也是比较容易,一个List数组,一个是普通数组,两者都需要遍历来转换,所以一个需要调用iterable
方法,一个需要调用array
方法。这里不对这两个方法做过多的解释,有兴趣的可以看看。
到这里,ServiceMethod
的基本分析也差不多,至于ServiceMethod
怎么将所有的相关信息转换成为一个Resquest
进行网络,怎么调用数据转换器进行数据转换,这些操作在后面我会详细的分析。
接下来,我们来分析平台适配器--CallAdapter
。
6. CallAdapter
大家还记得我们创建Retrofit对象时,通过调用Retrofit.Builder
的addCallAdapterFactory
方法添加了平台适配器的工厂类吗?接下来我们会详细的分析CallAdapter.Factory
,同时还有它的两个子类ExecutorCallAdapterFactory
和RxJava2CallAdapterFactory
。
我们先来看看CallAdapter.Factory
这个抽象类:
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); /** * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}. */ protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); } /** * Extract the raw class type from {@code type}. For example, the type representing * {@code List<? extends Runnable>} returns {@code List.class}. */ protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); } }
这只需要关注get
方法,至于getParameterUpperBound
和getRawType
,我相信大家都非常理解,毕竟注释写的非常清楚。
我们在Retrofit
的nextCallAdapter
方法里面通过调用CallAdapter.Factory
的get
方法来获取合适的CallAdapter
,怎么判断当前的CallAdapter是否合适呢?就是看这个get
方法返回的是否为null。
具体的我们来看看ExecutorCallAdapterFactory
和RxJava2CallAdapterFactory
。我们先来看看那RxJava2CallAdapterFactory
。
(1). RxJava2CallAdapterFactory
@Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { Class<?> rawType = getRawType(returnType); if (rawType == Completable.class) { // Completable is not parameterized (which is what the rest of this method deals with) so it // can only be created with a single configuration. return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false, false, true); } boolean isFlowable = rawType == Flowable.class; boolean isSingle = rawType == Single.class; boolean isMaybe = rawType == Maybe.class; if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) { return null; } boolean isResult = false; boolean isBody = false; Type responseType; if (!(returnType instanceof ParameterizedType)) { String name = isFlowable ? "Flowable" : isSingle ? "Single" : isMaybe ? "Maybe" : "Observable"; throw new IllegalStateException(name + " return type must be parameterized" + " as " + name + "<Foo> or " + name + "<? extends Foo>"); } Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType); Class<?> rawObservableType = getRawType(observableType); if (rawObservableType == Response.class) { if (!(observableType instanceof ParameterizedType)) { throw new IllegalStateException("Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>"); } responseType = getParameterUpperBound(0, (ParameterizedType) observableType); } else if (rawObservableType == Result.class) { if (!(observableType instanceof ParameterizedType)) { throw new IllegalStateException("Result must be parameterized" + " as Result<Foo> or Result<? extends Foo>"); } responseType = getParameterUpperBound(0, (ParameterizedType) observableType); isResult = true; } else { responseType = observableType; isBody = true; } return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false); }
get
方法前面一系列判断,就是判断当前接口的方法是否支持RxJava适配。怎么才能表示支持RxJava呢?当然是接口方法返回的类型Observable呢。
因为我们可以看到这个判断
if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) { return null; }
如果返回类型不是Observable
或者Flowable
,这里都会返回null,表示当前的方法不支持RxJava。
如果支持RxJava,最后会返回RxJava2CallAdapter
的对象。我们来看看RxJava2CallAdapter
,我们在分析RxJava2CallAdapter
时,只需要关注adapt
方法即可。
@Override public Object adapt(Call<R> call) { Observable<Response<R>> responseObservable = isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call); Observable<?> observable; if (isResult) { observable = new ResultObservable<>(responseObservable); } else if (isBody) { observable = new BodyObservable<>(responseObservable); } else { observable = responseObservable; } if (scheduler != null) { observable = observable.subscribeOn(scheduler); } if (isFlowable) { return observable.toFlowable(BackpressureStrategy.LATEST); } if (isSingle) { return observable.singleOrError(); } if (isMaybe) { return observable.singleElement(); } if (isCompletable) { return observable.ignoreElements(); } return observable; }
此时跟Retrofit
的create
方法就串联起来了。在Retrofit
的create
方法里面,最后一行代码就是调用这里的adapt
方法。
在RxJava2CallAdapter
的adapt
方法里面,究竟为我们做了什么?从代码中看来,其实根据情况,给我们创建了一个Observable
对象。
这里,我们还需要注意的是adapt
方法的参数是一个Call
对象,这个Call
对象是什么,用来干嘛的?在Retrofit
的create
方法里面,我们可以知道,这里的Call
对象就是OkHttpCall
对象,这个Call
对象肯定是用来进行网络请求的。这个操作,肯定是在Observable
里面。这里我们来挑一个Observable类看看。注意,接下来的分析都是跟RxJava相关的,如果不懂RxJava原理的同学,肯定感觉很吃力。
这里我们就挑相对难一点的CallEnqueueObservable
来看看。
在看Observable
的源码时,我们需要关注那个方法呢?当然是subscribeActual
方法,因为最终会执行到它来,我们来看看subscribeActual
方法:
@Override protected void subscribeActual(Observer<? super Response<T>> observer) { // Since Call is a one-shot type, clone it for each new observer. Call<T> call = originalCall.clone(); CallCallback<T> callback = new CallCallback<>(call, observer); observer.onSubscribe(callback); call.enqueue(callback); }
整个过程的简单,熟悉RxJava和OkHttp的同学肯定感觉非常亲切,最终进行网络请求的就是最后一行代码:
call.enqueue(callback);
由于这里是异步的,所以需要CallBack
对象来进行回调。我们来看看CallBack
的onResponse
方法:
@Override public void onResponse(Call<T> call, Response<T> response) { // 省略代码 observer.onNext(response); // 省略代码 }
我们这里就是将网络请求返回的Response
发送给订阅者。
这个就是整个RxJava2CallAdapter
的执行流程。是不是感觉Retrofit
和RxJava
天衣无缝的结合起来了?哈哈,这就是Retrofit
的魅力所在。
至于Call
的enqueue
方法到底做了什么,后面我会详细的分析。
接下来我们看看ExecutorCallAdapterFactory
。相信理解了RxJava2CallAdapterFactory
的工作流程,ExecutorCallAdapterFactory
就不会很难了。
(2). ExecutorCallAdapterFactory
为了少说废话,这里直接看ExecutorCallAdapterFactory
的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); } }; }
在这里,我们发现它只会new了一个CallAdapter
,同时实现了adapt
方法。在adapt
方法里面返回了一个ExecutorCallbackCall
对象。接下来的ExecutorCallbackCall
将Java的静态代理使用的出神入化。
我先来简单的分析整个ExecutorCallbackCall
整个结构。
首先,如果我们在接口定义这个这种类型方法,最终会调用这个的CallAdapter
的adapt
方法:
@POST("getSongPoetry") @FormUrlEncoded Call<Bean> getCall(@Field("page") int page, @Field("count") int count);
注意这里返回类型不是Observable
,而是Call
,这里的Call
是什么的对象。我们通过Retrofit
的create
方法获取一个代理对象,代理对象调用getCall
方法,返回的对象在这里,既不是OKHttpCall
,也不是OkHttp
里面的Call
对象,而是这里ExecutorCallbackCall
对象。例如,调用下面的enqueue
方法也是调用ExecutorCallbackCall
的enqueue
方法:
service.getCall(1, 20) .enqueue(new Callback<Bean>() { @Override public void onResponse(Call<Bean> call, Response<Bean> response) { } @Override public void onFailure(Call<Bean> call, Throwable t) { } });
而ExecutorCallbackCall
本身就是一个外壳,在ExecutorCallbackCall
的enqueue
方法里面调用了内部的一个代理对象的enqueue
方法。我们来看看:
@Override public void enqueue(final Callback<T> callback) { checkNotNull(callback, "callback == null"); delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> response) { callbackExecutor.execute(new Runnable() { @Override public void run() { 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(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); } }); } }); }
这个delegate
是什么呢?它就是OkHttpCall
对象。我们发现,在delegate
的Callback
里面使用callbackExecutor
将当前的数据调度到主线程去。
至于为什么调用execute
就会把当前的Response发送到主线程去,这里就不详细的解释了,有兴趣的可以看看Platfrom.Android.MainThreadExecutor
的execute
方法干了什么。
CallAdapter
的部分,我们的分析就到此为止,接下来我们来分析一下OkHttpCall
到底做了什么。
7. 网络执行器--Call
不论是接口返回返回类型为Observable
,而在Observable
的subscribeActual
方法会调用了Call
的enqueue
方法进行网络请求;还是返回类型为Call
,我们直接调用enqueue
方法来进行网络请求。我们都会发现,enqueue
是整个Call
重中之重。
我们先来看看Call
--OkHttpCall
。在Retrofit里面,这里的Call
就是OkHttpCall
。
跟OkHttp里面一样,这里OkHttpCall
也分为同步请求和异步请求,分别对应的是execute
方法和enqueue
方法。我们先来一个一个的来分析。
(1). 同步请求- execute
我们来看看execute
方法的源代码:
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { if (creationFailure instanceof IOException) { throw (IOException) creationFailure; } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } call = rawCall; if (call == null) { try { call = rawCall = createRawCall(); } catch (IOException | RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. creationFailure = e; throw e; } } } if (canceled) { call.cancel(); } return parseResponse(call.execute()); }
整个execute方法的过程,我将它分为3步:
1. 创建
rawCall
,也就是OkHttp
里面的call
。
2. 调用rawCall
的execute
方法进行真正的网络请求。
3. 调用parseResponse
方法解析Response。这里面会使用到数据转换器,这个在后面会详细的分析。
还是按着老规矩来,一步一步的来分析。
首先是创建rawCall
对象。这一步最终是调用了ServiceMethod
的toCall
方法。
okhttp3.Call toCall(@Nullable Object... args) throws IOException { RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types. ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; int argumentCount = args != null ? args.length : 0; if (argumentCount != handlers.length) { throw new IllegalArgumentException("Argument count (" + argumentCount + ") doesn't match expected count (" + handlers.length + ")"); } for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); } return callFactory.newCall(requestBuilder.build()); }
创建rawCall
对象的过程跟OkHttp
里面创建一个Call
是一样的,这里我们只需关注下面的代码就行了。
for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); }
这里调用ParameterHandler
的apply
方法将每个参数转换成为网络请求真正需要的数据,这里与前面我们分析parseParameter
方法不谋而合。
创建好了rawCall
对象,此时就应该调用rawCall
的execute
方法来进行网络请求。这里的原理就是OkHttp
的知识,这里就不在展开了,有兴趣的同学,可以参考我的OkHttp
源码分析系列-OkHttp 源码分析系列。
最后就是调用parseResponse
方法来进行数据转化。我们来简单看看parseResponse
方法
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // Remove the body's source (the only stateful object) so we can pass the response along. rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); int code = rawResponse.code(); if (code < 200 || code >= 300) { try { // Buffer the entire body to avoid future I/O. ResponseBody bufferedBody = Utils.buffer(rawBody); return Response.error(bufferedBody, rawResponse); } finally { rawBody.close(); } } if (code == 204 || code == 205) { rawBody.close(); return Response.success(null, rawResponse); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { // If the underlying source threw an exception, propagate that rather than indicating it was // a runtime exception. catchingBody.throwIfCaught(); throw e; } }
在parseResponse
方法里面有很多的判断,这里我们不需要有太多的关注,只需要看下面这一行代码就行了:
T body = serviceMethod.toResponse(catchingBody);
通过调用ServiceMethod
的toResponse
方法将RequestBody
转换成为我们需要的Bean对象。我们来看看ServiceMethod
的toResponse
方法。
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
这里没有过多的操作,只是简单调用Converter
的convert
方法来进行数据转换。这里先不对Converter
做过多的解释,待会会详细的分析它。
整个三步执行完了,就是OKHttpCall
的execute
执行完毕。不得不说Retrofit的代码设计是多么的优秀,整个过程酣畅淋漓,一切都感觉合情合理的。
接下里,我们来看看异步请求的过程。
(2). 异步请求- enqueue
异步请求跟同步请求比较起来,只是多了一个Callback
参数。而这个Callback就是我们在调用enqueue
方法传入的Callback
,这里就不对enqueue
做过多的解释了。
接下来,我们分析一下Retrofit最后一个部分Converter
--数据转换器
8.数据转换器--Converter
在创建Retrofit对象时,我们通过调用Retrofit.Builder
的addConverterFactory
添加了一个数据转换器。我们来看看代码:
private Retrofit mRetrofit = new Retrofit.Builder() .baseUrl("https://api.apiopen.top/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();
我们直接来看看Converter.Factory
这个类:
abstract class Factory { /** * Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null if * {@code type} cannot be handled by this factory. This is used to create converters for * response types such as {@code SimpleResponse} from a {@code Call<SimpleResponse>} * declaration. */ public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return null; } /** * Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if * {@code type} cannot be handled by this factory. This is used to create converters for types * specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap} * values. */ public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { return null; } /** * Returns a {@link Converter} for converting {@code type} to a {@link String}, or null if * {@code type} cannot be handled by this factory. This is used to create converters for types * specified by {@link Field @Field}, {@link FieldMap @FieldMap} values, * {@link Header @Header}, {@link HeaderMap @HeaderMap}, {@link Path @Path}, * {@link Query @Query}, and {@link QueryMap @QueryMap} values. */ public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return null; } /** * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}. */ protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); } /** * Extract the raw class type from {@code type}. For example, the type representing * {@code List<? extends Runnable>} returns {@code List.class}. */ protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); } }
这里需要重点关注三个方法,一个是responseBodyConverter
,另一个方法是requestBodyConverter
,还有一个是stringConverter
。前两个方法我们从名字上就可以知道是什么意思,一个将获取Response
的转换器;一个是获取Request
的转换器;stringConverter
方法,我们在将每个参数解析成为ParameterHandler
对象看到过,这个方法的作用主要将参数转换成为网络请求需要的配置参数。
具体的实现,我们来看GsonConverterFactory
,在这里,我们重点分析一下responseBodyConverter
方法,requestBodyConverter
方法跟responseBodyConverter
是一样的原理。
@Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); return new GsonResponseBodyConverter<>(gson, adapter); }
这里创建了一个GsonResponseBodyConverter
对象,我们来看看这个类:
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 { T result = adapter.read(jsonReader); if (jsonReader.peek() != JsonToken.END_DOCUMENT) { throw new JsonIOException("JSON document was not fully consumed."); } return result; } finally { value.close(); } }}
整个GsonResponseBodyConverter
非常的简单,在ServiceMethod
的toResponse
方法里面,就是调用了convert
方法将Response
的body
转换成为我们的Bean
对象。
而我们从这里可以看出来,整个convert
方法非常的简单,没有做什么难以理解的操作,实际上,所有的解析工作都是由Gson给我们完成的。从这里来看,Gson确实强大,不愧是Google爸爸的东西。这里就不对Gson做过多的解释,因为我也不懂?。
9. 总结
最后,我们对Retrofit做一个简单的总结。
1. Retrofit大体上分为4个部分:接口、
Request
(ServiceMethod
)、网络请求执行器Call
、数据转换器Converter
。每个部分都有自己的职责。
2.Retrofit
里面最核心的一个类就是ServiceMethod
。这个类负责解析方法,生成对应的Request
,转换Response。
3. Retrofit 内部使用太多太多的设计模式,是学习设计模式的不二典范。
原著是一个有趣的人,若有侵权,请通知删除
还没有人抢沙发呢~