Retrofit源码分析
Retrofit简介
是Square公司基于Okhttp封装的一款网络开源框架,简化了对网络的请求。
以下基于Retrofit2.1.0版本的分析,本文仿写 码老板的博客https://zhuanlan.zhihu.com/p/35121326关于“Retrofit原理解析最简洁的思路”。。
Retrofit使用
定义接口请求参数
public interface ApiService {
@GET("app/{volumeId}/updateDefaultVolume")
Call<ResponseBody> updateDefaultVolume(@Path("volumeId") String volumeId);
}
通过以上方式简化了网络请求,代码也更直观
实例化Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("xxx")
.build();
以上是最简单的Retrofit初始化方式,采用了链式调用的设计
获取接口实现类
ApiService apiService = retrofit.create(ApiService.class);
Call<ResponseBody> call = apiService.updateDefaultVolume("oo121o");
通过retrofit的create方法,获取到接口的实现类,接下来再调用自己定义的方法,进行网络请求。但是我们只定义了一个接口,并没有方法体,请求方式和参数都还是注解,那么方法体它是怎么生成的,接下来源码会具体进行讲解
进行网络请求
//同步网络请求
Request request = call.request();
//异步网络请求
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
以上就是retrofit的最简单的网络请求方法,由于是基于Okhttp的封装,其网络请求变得简洁。接下来具体分析起原理
Retrofit原理分析
retrofit初始化
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
首先看Builder()源码
public Builder() {
this(Platform.get());
}
this表示其调用自己的构造方法,如下
Builder(Platform platform) {
this.platform = platform; //====1
// 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()); //====2
}
1处就是简单的赋值,2处是转换器工厂添加各种类型转换器(暂时是这样理解),
点开上面的Platform.get()方法,
private static final Platform PLATFORM = findPlatform();//2
static Platform get() {
return PLATFORM; //1
}
private static Platform findPlatform() {//3
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("org.robovm.apple.foundation.NSObject");
return new IOS();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
从上述代码可以发现get()方法是典型的饿汉式单例,这样写的好处是简单、线程安全、效率高、不会生成多个实例。1和2构成了典型的饿汉式单例。3就是判断系统,根据系统然后实例化不同的平台对象。这里跟我们相关的只有实例化Android这块,但是,在这不进行深究,略过。。。
baseUrl(“https://api.github.com”)源码分析
点开baseUrl(“https://api.github.com”)方法,查看其实现
/**
* Set the API base URL.
*
* @see #baseUrl(HttpUrl)
*/
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null"); //1
HttpUrl httpUrl = HttpUrl.parse(baseUrl); //2
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl); //3
}
1处是对baseUrl进行判空处理,这个没啥可讲的。
2处是对baseUrl进行解析,可以进去查看parse方法,里面基本都是一些对路径的规范化判定
3处传入了一个HttpUrl对象,查看其实现,如下
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();//获取到路径段列表
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { //路径结尾必须以“/”结尾
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
这个方法主要是给baseUrl赋值,同时规范baseUrl的路径展示形式,结尾必须以“/”结束。否则,会报错。。
总结,由以上可知,baseUrl方法主要是对路径进行了规范的判定,同时进行赋值。
build()方法源码解析
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
这个方法主要是创建Retrofit的一个实例。并初始化一些参数对象。callFactory是okhttp的工厂实例,用于网络请求的,converterFactories是转换器工厂集合,adapterFactories是回调适配器工厂集合,callbackExecuto应该是回调接口实例,在这个方法中对这些对象进行了实例化,并最终返回Retrofit的实例。。。
接口实现类源码分析
ApiService apiService = retrofit.create(ApiService.class);
Call<ResponseBody> call = apiService.updateDefaultVolume("oo121o");
通过调用Retrofit的create方法获取到接口的实例,那么为什么调用create方法会生成接口实例??点击打开create方法源码
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service); //验证传入的class是否标准的接口类
if (validateEagerly) {
eagerlyValidateMethods(service); //把接口类里面的定义方法加入LinkedHashMap集合中
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
最后一段代码Proxy.newProxyInstance…采用了 动态代理 的设计模式,而且这个方法封装得非常好,我么只需要传入相应的接口类,就能获取到其实例。遵循了 迪米特原则(最少知道原则)。
代理也称“委托”,分为静态代理和动态代理,代理模式也是常用的设计模式之一,具有方法增强、高扩展性的设计优势。其设计就是限制对象的直接访问。。动态代理是JDK提供的代理方式且只支持接口,在JVM虚拟机运行时动态生成一系列代理,主要通过Java提供的InvocationHanler类实现。写一个类实现InvocationHanler接口,实现invoke方法,调用Proxy.newProxyInstance返回实例。
Retrofit在重写这个方法时,主要做了三件事
1.判断该接口类是不是一个Object.class,就直接返回方法原有的返回值
2.判断该方法是否是DefaultMethod,
3.构建一个ServiceMethod对象和OkHttpCall对象,并通过serviceMethod.callAdapter.adapt(okHttpCall)将这两个对象关联起来。点开adapt,发现其是CallAdapter接口类的一个方法,如下
<R> T adapt(Call<R> call);
那这个方法是在那里进行实现的呢,在上面讲解build()源码方法时,即Retrofit实例化时,有下面一段代码:
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
而adapterFactories其实就是CallAdapter的一个工厂类集,点开defaultCallAdapterFactory方法,如下
CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
if (callbackExecutor != null) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
return DefaultCallAdapterFactory.INSTANCE; //获取到默认的CallAdapter的工厂类
}
//接下来打开DefaultCallAdapterFactory类,发现其是继承自CallAdapter.Factory
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
@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<Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public <R> Call<R> adapt(Call<R> call) {
return call; //1在这里CallAdapter的接口方法adapt被重写,并返回Call的类型
}
};
}
}
上述代码看出,在Retrofit初始化实例的时候,CallAdapter接口方法adapt就已经被重写了,并且 返回的Call的类型。
到这里为止,我们就知道了网络请求结果返回的是Call类型。
请求参数以及请求方式的解析
在我们上面提到的create方法中有这么一段代码,如下
ServiceMethod serviceMethod = loadServiceMethod(method);
这段代码主要就是对接口参数以及请求方式的解析,其中method表示接口方法,点开loadServiceMethod方法,查看其具体实现。
ServiceMethod loadServiceMethod(Method method) {//传入一个方法体
ServiceMethod result;
synchronized (serviceMethodCache) { //采用同步锁,保证访问serviceMethodCache对象的唯一性
result = serviceMethodCache.get(method); //从serviceMethodCache这个hashmap中取method
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
从以上代码可知,其核心的代码就是result = new ServiceMethod.Builder(this, method).build()这句,点开Builder(this, method).法,代码如下
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();//接口方法的注解,在Retrofit中为请求方式
this.parameterTypes = method.getGenericParameterTypes();//参数类型
this.parameterAnnotationsArray = method.getParameterAnnotations();//参数注解数组
}
以上代码主要是对一些对象进行了实例化,并没有什么核心操作,接下来看build()这个方法。代码如下
public ServiceMethod build() {
callAdapter = createCallAdapter(); //(1)
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(); //(2)
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation); //(3)
}
...省略代码...
//(4)======
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.");
}
//(5)===
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
...省略代码...
return new ServiceMethod<>(this);
}
从上述代码,并查看其相关源码可知,感兴趣的可以自行查看,
(1).初始化了一个可用的CallAdapter实例,循环遍历adapterFactories工厂,取出可用的CallAdapter对象
(2).初始化了一个可用的Converter<ResponseBody, T>实例,循环遍历converterFactories工厂,取出可用的Converter对象
(3).parseMethodAnnotation(annotation)用来解析注解的请求方式,这里我们只讲解GET请求方式,点开查看其源码。其它 请求方式相似。如下所示
private void parseMethodAnnotation(Annotation annotation) {
...省略代码...
if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
}
...省略代码...
}
打开parseHttpMethodAndPath方法,查看其实现
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError("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.
//校验value的值是否合法,规则就是不能有“?”如果有则需要使用@Query注解
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("URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
this.relativeUrl = value;//赋值给相对路径relativeUrl,相当于省略域名的URL
this.relativeUrlParamNames = parsePathParameters(value);//解析路径参数
//到这里,我们能得到app/{volumeId}/updateDefaultVolume这样的一个路径,大括号里面的值就是我们需要赋值的参数。
}
(4).先获取注解数组parameterAnnotationsArray的长度parameterCount,然后遍历循环parameterAnnotationsArray数组,获取参数类型以及参数注解。
(5).把上面获取的参数类型和注解放在同一个方法中进行解析,
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
parameterType:参数类型
parameterAnnotations:参数注解
通过parseParameter方法对parameterType和parameterAnnotations进行解析,并返回ParameterHandler<?>对象,赋值给对应的原始数组对象。那么parseParameter到底是怎么解析的呢,我们来打开parseParameter这个方法,
private ParameterHandler<?> parseParameter(
int p, Type parameterType, Annotation[] annotations) {
ParameterHandler<?> result = null;
for (Annotation annotation : annotations) {
ParameterHandler<?> annotationAction = parseParameterAnnotation(
p, parameterType, annotations, annotation);
...省略代码...
}
...省略代码...
return result;
}
发现还是遍历注解,且赋值。继续打开parseParameterAnnotation方法,当我们查看parseParameterAnnotation( p, parameterType, annotations, annotation)这个方法时,发现其代码量多达400行左右,且里面逻辑大多一致,我们就以Path参数注解为例讲解。
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
...省略代码...
if (annotation instanceof Path) {
...省略代码...
Path path = (Path) annotation;
String name = path.value(); //获取到参数名,即获取@Path("volumeId")中的参数volumeId,
validatePathName(p, name); //验证参数名是否合法
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(name, converter, path.encoded());
}
...省略代码...
return null; // Not a Retrofit annotation.
}
重点关注stringConverter(type, annotations)这个方法,点开stringConverter方法,
public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
for (int i = 0, count = converterFactories.size(); i < count; i++) {
Converter<?, String> converter =
converterFactories.get(i).stringConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<T, String>) converter;
}
}
// Nothing matched. Resort to default converter which just calls toString().
//noinspection unchecked
return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
}
首先它会循环遍历转换器工厂converterFactories这个数组,以其能从converterFactories中获取到Converter<?, String>对象,并返回。但是,继续点开stringConverter(type, annotations, this)这个方法。
public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
你会发现它一直是返回为null的,所以stringConverter(type, annotations)这个方法会执行接下来的这个方法,(Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE,可以看出它应该调用的是一个静态对象,点开ToStringConverter类,发现其是一个接口实现类,用来重写Converter<Object, String>接口的convert方法,并返回一个字符串,这个字符串其实就是参数注解名。
static final class ToStringConverter implements Converter<Object, String> {
static final ToStringConverter INSTANCE = new ToStringConverter();
@Override public String convert(Object value) {
return value.toString();
}
}
那么Converter<Object, String>这个接口,它是在哪里进行实现的呢,接下来我们要讲new ParameterHandler.Path<>(name, converter, path.encoded())这个方法,而Converter<Object, String>接口的实现就在这个方法中。点开Path,查看其源码
static final class Path<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Path(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, T value) throws IOException {
if (value == null) {
throw new IllegalArgumentException(
"Path parameter \"" + name + "\" value must not be null.");
}
builder.addPathParam(name, valueConverter.convert(value), encoded);
}
}
Path里面首先市实例化了一些对象:参数名name,转换器接口实例valueConverter,是否编码encoded。
接下来重写了ParameterHandler这个抽象类里面的apply方法,在这个方法里面有一个builder.addPathParam(name, valueConverter.convert(value), encoded)方法,其中有传入一个参数 valueConverter.convert(value),valueConverter它是Converter<Object, String>接口的实例对象,而convert就是接口方法,所以,Converter<Object, String>接口的调用就是在这里。然后,接下来,我们分析addPathParam这个方法用来干啥的,点开addPathParam方法,发现它是对路径进行了一个替换处理,
void addPathParam(String name, String value, boolean encoded) {
if (relativeUrl == null) {
// The relative URL is cleared when the first query parameter is set.
throw new AssertionError();
}
relativeUrl = relativeUrl.replace("{" + name + "}", canonicalizeForPath(value, encoded));
}
这个方法把我们传进来的值value
按照编码格式转换,然后替换relativeUrl
中的{name}
,构成一个有效的省略域名的URL。至此,URL的拼接已经完成!
Retrofit网络请求
Retrofit的网络请求其实在创建接口实例化的时候,就已经开始了,动态代理创建时,其实现方法invoke里面会实例一个OkHttpCall对象okHttpCall,点开会发现OkHttpCall这个类是用来实现Call这个接口的,然后重写了Call里面的方法。这里只分析接口Call的enqueue方法。即异步网络请求方式。
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
这里只展示enqueue方法
final class OkHttpCall<T> implements Call<T> {
。。。
OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
。。。
}
}
这里关注call的创键时机,通过上述代码可知,createRawCall方法它会返回一个call的对象,打开createRawCall查看其源码。
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
createRawCall方法中首先,它会创建一个Http Request的请求对象request,接下来它会调用Call.Factory接口中的newCall方法,并返回一个Call对象,注意,Call.Factory接口中方法实现在Retrofit的build()方法中就已经被重写了,如下build()方法中的一段,初始化OkHttpClient对象。。
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
OkHttpClient重写了Call.Factory接口
public class OkHttpClient implements Cloneable, Call.Factory {
。。。
}
总结:Retrofit主要是在create方法中采用动态代理模式实现接口方法,这个过程构建了一个ServiceMethod对象,根据方法注解获取请求方式,参数类型和参数注解拼接请求的链接,当一切都准备好之后会把数据添加到Retrofit的RequestBuilder中。然后当我们主动发起网络请求的时候会调用okhttp发起网络请求,okhttp的配置包括请求方式,URL等在Retrofit的RequestBuilder的build()方法中实现,并发起真正的网络请求