Retrofit是square公司开源的一款类型安全的http请求框架,用于Java和Android程序。Retrofit可以说是restful风格的一个代表作,可能有人对于restful设计风格不太熟悉,在此有必要做一下解释;restful(Representational State Transfer):表现层状态转化,结合这里的Retrofit来理解就是,将基于http协议的网络请求转化成对象的方法调用的形式,让使用者再也不必自己写繁重的http请求过程。实际上,Retrofit本身并没有进行网络请求操作,真正的网络请求操作是依赖于Retrofit内部使用的Okhttp实现的,换句话说,就是Retrofit在Okhttp的基础之上又进行了一次封装,将Okhttp的使用方式转换成了对象的方法调用的形式;这样,复杂的http请求就变成了简单的方法调用,有没有感觉很神奇。下面,我们逐步来拆解这个巨大而又神奇的车轮子。
下面先来看一下实际使用的例子,然后根据例子逐步分析
//服务接口类
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
//发起请求
public void requestHttp(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHub github = retrofit.create(GitHub.class);
Call> call = github.contributors("square", "retrofit");
}
以上就是一个使用Retrofit发起http请求的完整的例子,首先,定义一个服务接口类,声明要发起请求的方法;然后在需要发起网络请求的地方生成一个retrofit实例,利用retrofit.create()生成一个服务接口类的对象github,调用github对象的方法,这样就完成了这次网络请求,并且拿到了从服务器返回的数据。这也太简单了,到底是怎么实现的呢?
根据上面的例子可以推断,Retrofit这个类是以构建者模式实现的,但是如果从整个框架的角度来看,它应用的是外观模式。先来看看它的可配置项
public static final class Builder {
private final Platform platform;//运行平台,Java/Android
private @Nullable okhttp3.Call.Factory callFactory;//请求工厂,实际上就是OkhttpClient
private HttpUrl baseUrl;
private final List converterFactories = new ArrayList<>();//转换工厂集合,如Gson、Jackson等序列化转换
private final List callAdapterFactories = new ArrayList<>();//call适配器工厂集合,如RxJava2CallAdapterFactory
private @Nullable Executor callbackExecutor;//请求的响应回调执行器
private boolean validateEagerly;//提前配置服务接口对象方法
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
Builder(Retrofit retrofit) {
platform = Platform.get();
callFactory = retrofit.callFactory;
baseUrl = retrofit.baseUrl;
converterFactories.addAll(retrofit.converterFactories);
// Remove the default BuiltInConverters instance added by build().
converterFactories.remove(0);
callAdapterFactories.addAll(retrofit.callAdapterFactories);
// Remove the default, platform-aware call adapter added by build().
callAdapterFactories.remove(callAdapterFactories.size() - 1);
callbackExecutor = retrofit.callbackExecutor;
validateEagerly = retrofit.validateEagerly;
}
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();
}
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
List converterFactories =
new ArrayList<>(1 + this.converterFactories.size());
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
根据Retrofit#Builder.build()方法实现可知,Retrofit控制器有如下可配置项:
配置完以上数据,最后new出了一个Retrofit对象,至此,生成了Retrofit对象。
回顾我们前面的示例可知,创建好retrofit后,需要调用Retrofit.create()方法生成服务接口对象,那又是如何来创建服务接口对象的呢?带着这个疑问,我们来到Retrofit.create()的世界:
//Retrofit#create()
public T create(final Class 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 (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args);
}
});
}
在create方法中,首先,会先检查传入的service是否满足接口的要求;然后,会根据我们配置的validateEagerly属性来判断是否提前加载service中声明的方法,如果需要提前加载,就会调用eagerlyValidateMethods()方法,该方法利用反射来获取我们在service中声明的方法,并将所有方法缓存在serviceMethodCache集合中,至于获取方式我们后面会介绍;如果不需要提前加载,会在调用方法的时候再加载;最后,通过动态代理的方式来实现http的请求,返回一个代理对象,这也说明了我们为什么要用接口来做http请求,并且要进行接口检查。当我们通过返回的代理对象调用我们声明的方法时,会执行InvocationHandler中的invoke()方法,在该方法中会判断执行的方法是否是普通对象的方法或者是平台默认的方法,如果均不是,则会执行loadServiceMethod()方法获得ServiceMethod对象,然后执行invoke()方法。这里获得ServiceMethod对象的方式即为前面提到的获取service中声明的方法的方式。下面我来看看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 = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
实现逻辑很简单,先检查缓存中是否有method对应的ServiceMethod对象(ServiceMethod对象就是服务接口类中声明的方法,两者一一对应),如果有就返回这个对象,如果没有就通过ServiceMethod.parseAnnotations()生成这个对象(这里要注意线程同步问题),然后将这个ServiceMethod对象加入到缓存中。由此可见,Retrofit.loadServiceMethod()将获取ServiceMethod对象的实现委托给ServiceMethod.parseAnnotations()实现,根据字面意思可推测,ServiceMethod通过解析注解来获取相应对象。
ServiceMethod可以理解为将服务接口类的方法转化为网络请求Call的转化类,传入的参数为retrofit何method,然后通过反射原理解析method,得到其实例。我们深入到这个类的内部去看看
abstract class ServiceMethod {
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return new HttpServiceMethod.Builder
这是一个抽象类,由一个解析注解的静态方法和一个抽象方法组成。这个抽象方法就是在前面生成动态代理对象后,最终要调用其发起网络请求的方法,也就是说ServiceMethod子类的invoke()方法是真正发起网络请求的地方。而这里所说的ServiceMethod子类对象由这个静态方法所提供。由上面代码可知,解析过程分为两步,第一步是检查方法的返回值类型是否符合要求,第二步是通过HttpServiceMethod#Builder这个构建者模式来生成一个HttpServiceMethod对象。显然,ServiceMethod解析注解的过程又交给了HttpServiceMethod实现,下面我们就来看看解析注解的过程,这也是这个库的核心所在。
//HttpServiceMethod#Builder 可配置项
static final class Builder<ResponseT, ReturnT> {
final Retrofit retrofit;
final Method method;
RequestFactory requestFactory;
Type responseType;
Converter responseConverter;
CallAdapter callAdapter;
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
}
HttpServiceMethod build() {
requestFactory = RequestFactory.parseAnnotations(retrofit, method);
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError(method, "'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
responseConverter = createResponseConverter();
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
return new HttpServiceMethod<>(this);
}
private CallAdapter createCallAdapter() {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
return (CallAdapter) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
private Converter createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType);
}
}
}
根据上面的代码可知,这个HttpServiceMethod的Builder是如何创建HttpServiceMethod对象的,一共由四个过程完成:第一,通过请求工厂RequestFactory解析注解,拿到requestFactory对象,这个对象包含了发起请求的所有数据;第二,根据方法返回值类型和注解,创建callAdapter对象;第三,获取响应类型,根据响应类型和注解
创建responseConverter对象,第四,根据该Builder创建HttpServiceMethod对象实例。关于这四个过程实现的细节,我们稍后再作介绍。HttpServiceMethod是ServiceMethod的子类,实现了invoke()方法,也就是真正发起网路请求的方法,我们先来看下这个方法的实现
final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
private final RequestFactory requestFactory;
private final okhttp3.Call.Factory callFactory;
private final CallAdapter callAdapter;
private final Converter responseConverter;
HttpServiceMethod(Builder builder) {
requestFactory = builder.requestFactory;
callFactory = builder.retrofit.callFactory();
callAdapter = builder.callAdapter;
responseConverter = builder.responseConverter;
}
@Override ReturnT invoke(@Nullable Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
}
使用requestFactory,args,callFactory和responseConverter创建了一个OkHttpCall对象,然后将其作为参数传递给callAdapter.adapt()方法,这样就生成了一个可以发起请求的Call对象。要想知道这个callAdapter具体实现,就需要分析上面创建HttpServiceMethod的四个步骤,下面我们逐个来分析:
一. 通过请求工厂RequestFactory解析注解,拿到RequestFactory对象,解析过程是什么样的呢?
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
...
static final class Builder {
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(method,
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError(method, "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(method, p,
"Parameter type must not include a type variable or wildcard: %s", parameterType);
}
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(method, p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
if (relativeUrl == null && !gotUrl) {
throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError(method, "Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError(method, "Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError(method, "Multipart method must contain at least one @Part.");
}
return new RequestFactory(this);
}
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} 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);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
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);
if (queryParamMatcher.find()) {
throw methodError(method, "URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
}
}
RequestFactory通过parseAnnotations()方法解析注解,而在该方法的内部,则是直接通过RequestFactory#Builder来生成一个RequestFactory对象,我们跳过可配置项,直接看build()方法。build()方法内部逻辑也很清晰,分为三个过程,第一,解析方法注解,通过parseMethodAnnotation()实现;第二,解析参数,通过ParameterHandler实现,第三,根据该builder对象创建RequestFactory。至此,HttpServiceMethod创建requestFactory的整体流程已经完成,其内部解析注解的细节,我们后面再做介绍。
二. 根据返回值类型和注解,获取callAdapter,先来看下创建过程
//HttpServiceMethod.createCallAdapter()
private CallAdapter createCallAdapter() {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
try {
return (CallAdapter) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) {
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
//Retrofit.callAdapter()
public CallAdapter, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
//Retrofit.nextCallAdapter()
public CallAdapter, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
...
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;
}
}
...
throw new IllegalArgumentException(builder.toString());
}
HttpServiceMethod通过createCallAdapter()来获取自身属性值callAdapter,而该方法内部又分别链式调用了Retrofit.callAdapter()和Retrofit.nextCallAdapter()方法来获取callAdapter值,而在nextCallAdapter()方法中,通过摘选我们初始化Retrofit时配置的callAdapterFactories,获得匹配的callAdapter,摘选的条件是returnType(返回值类型)和annotations(注解)两个参数。回顾Retrofit对象的创建过程可知,Retrofit本身提供了默认的DefaultCallAdapterFactory,而在Android平台上,默认提供了ExecutorCallAdapterFactory,下面我们就来看看Android平台默认的ExecutorCallAdapterFactory的实现逻辑
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
final Executor callbackExecutor;
ExecutorCallAdapterFactory(Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@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
在get()方法中,返回了CallAdapter对象,即我们上文提到的HttpServiceMethod的属性值callAdapter,而且实现了adapt()方法,是否还记得,上面我们分析过的,这个adapt()方法就是HttpServiceMethod真正获取请求Call的地方,至此,便将执行逻辑转移到了这个adapt()方法中。在这个adapt()方法中,直接创建了一个ExecutorCallbackCall对象,参数分别是Android平台前面初始化时默认提供的callbackExecutor和上面生成的OkHttpCall。这个对象实际上是一个Call对象,实现了请求的各种方法,如enqueue()、execute()、request()等,在这些方法内部,将真正的请求委托给了OkHttpCall对象,也就是我们通过构造方法创建ExecutorCallbackCall时传递进来的OkHttpCall对象;而对于线程的切换,则由callbackExecutor将返回的数据发送到UI线程实现,实际上,这个callbackExecutor是Android平台提供的默认MainThreadExecutor的对象,内部通过Handler实现。至此,HttpServiceMethod中获取callAdapter的分析已完成。
三、根据返回值类型和注解,获取responseConverter,这个过程和上面第二步过程基本一致,在此不做详细分析,Retrofit提供的默认Converter.Factory是BuiltInConverters,读者自行分析时可以查阅。
四、HttpServiceMethod通过构造方法创建其自身对象。
上面分析HttpServiceMethod.invoke()方法时,只是简单的说明了new一个OkHttpCall,实际上,OkHttpCall继承自Call,这里的Call是Retrofit中的Call,跟OkHttp中的Call非常类似,主要是声明同步调用request()方法和异步调用enqueue()和其他状态判断方法。OkHttpCall中持有了okhttp3.Call对象,各个方法的实现均是委托给okhttp3.Call对象实现的。okhttp3.Call对象的获取方式如下
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
callFactory是配置Retrofit时创建的OkHttpClient对象,requestFactory.create(args)创建了一个Request,这样,就生成了一个完整的okhttp3.Call对象。
至此,整个流程分析完毕,总结如下:定义了一个包含网络请求的服务接口类,然后Retrofit利用动态代理机制创建了这个接口类的对象并返回,在接口类对象调用到它的方法时都会调用到这里的InvocationHandler对象的invoke方法,该方法有三个参数,proxy是动态代理生成的对象,method是调用到的方法对象,args是调用方法的参数。通过解析这个method并执行,达到发起网络请求的目的。