如果你还没了解Retrofit如何使用,可以先查看这篇文章:Retrofit使用指南
一般分析源码都习惯从使用开始分析,那么接下来先从实例化Retrofit
开始分析。
new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
此处使用了builder设计模式实例化Retrofit
,不管build()
方法里面执行了什么,但最终还是需要调用Retrofit
的构造函数的,那么我们看它的构造函数:
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
先不关心这些参数到底有什么作用,再回顾一下我们是如何使用Retrofit
请求接口的:
创建一个接口,通过申明方法来请求接口,
public interface TestService {
//定义一条接口请求
@GET("users/yuhengye")
Call<GitHubUser> getUserInfo();
}
调用请求接口:
TestService testService = getRetrofit().create(TestService.class);
Call<GitHubUser> call = testService.getUserInfo();
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<GitHubUser> call, Response<GitHubUser> response) {}
@Override
public void onFailure(Call<GitHubUser> call, Throwable t) {}
});
上面代码里是直接调用我们申明的接口方法testService.getUserInfo()
,但是接口申明的方法肯定都是未实现的,可以直接调用的话证明我们一定是实现了这个接口,然而我们根本没有实现过TestService
这个接口,我们是通过Retrofit
的create(TestService.class)
方法得到实现列TestService接口的实例,那么看一下create()
方法:
public <T> T create(final Class<T> service) {
//验证service是否是接口类型,并且没有继承任何接口
Utils.validateServiceInterface(service);
if (validateEagerly) {
//提前加载TestService申明的方法
eagerlyValidateMethods(service);
}
//通过动态代理创建TestService接口的实例
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
//具体代码实现下文再分析
});
}
如果想了解更多Java的动态代理知识的话可以去参考其他资料了解下,这里简单说明下调用过程,上面代码最后实例化了一个TestService
的动态代理,可以理解为这个动态代理实现了TestService
接口,每次调用接口里的方法并且返回值时,都会通过调用创建的InvocationHandler
的invoke()
方法返回值,也就是说当我们调用TestService
的getUserInfo()
得到的值是上面代码invoke()
方法里return的值,那分析一下invoke()
里发生了什么:
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.
// 如果申明该方法的类是Object, 则直接调用该方法
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 如果方法是平台默认的方法,转移给具体平台实现类调用,Android的平台实现类没有默认的方法;
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 加载接口的方法
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 通过CallAdapter的adapt()方法转换得到接口申明方法的值
return serviceMethod.callAdapter.adapt(okHttpCall);
}
}
首先开始调用了Platform.get()
得到了当前使用的平台,实际是调用了这个方法:
private static Platform findPlatform() {
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();
}
这段代码不难理解,通过Class.forName()
判断出当前所使用的平台,一共有Android
,Java8
,IOS
这3个平台,再看看Android
平台继承类的具体实现:
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
只是重载类Platform
的两个方法,不过可以看到里面申明了一个MainThreadExecutor
类,里面有用到Android SDK的Handler
实现在主线程执行Runnable
,那么我们在什么时候需要用到这个东西呢,回想一下我们在使用Retrofit
异步请求网络并且在回调里得到成功或是失败的结果,在这里肯定是需要在主线程执行回调的,不然使用起来还要自己切换主线程更新UI,那么到底是不是,我们下文通过源码验证。
回到上文提到的InvocationHandler
,当执行到if(platform.isDefaultMethod(method))
时,因为Platform
默认都返回false,只有Java8
才会走这一步,此处略过,接下来到ServiceMethod serviceMethod = loadServiceMethod(method);
:
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
// 从Map缓存中获取方法对应的ServiceMethod,没有则实例化,并且放进缓存中
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
此方法重点是实例化ServiceMethod
,主要用到的参数是Retrofit
和接口方法Method
,Map缓存中把Method
作为Key,证明一个接口方法对应着一个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);
}
/...此处是一些参数验证等,具体实现请查看源码.../
return new ServiceMethod<>(this);
}
最终调用ServiceMethod
的构造函数:
ServiceMethod(Builder<T> builder) {
this.callFactory = builder.retrofit.callFactory();
this.callAdapter = builder.callAdapter;
this.baseUrl = builder.retrofit.baseUrl();
this.responseConverter = builder.responseConverter;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.headers = builder.headers;
this.contentType = builder.contentType;
this.hasBody = builder.hasBody;
this.isFormEncoded = builder.isFormEncoded;
this.isMultipart = builder.isMultipart;
this.parameterHandlers = builder.parameterHandlers;
}
以上变量大多从变量名字就可以猜测出其含义,当InvocationHandler
的invoke()
方法通过loadServiceMethod(method)
解析接口申明的方法后,最后会执行以下代码:
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
而OkHttpCall
的构造函数是这样的:
final class OkHttpCall<T> implements Call<T> {
private final ServiceMethod<T> serviceMethod;
private final Object[] args;
OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
/...其余代码略过.../
}
先实例化一个OkHttpCall
,最后通过调用ServiceMethod
的成员变量callAdapter
的adapt
方法得到我们在接口申明方法里定义的返回值,而OkHttpCall
继承了Retrofit
的Call
接口:
public interface Call<T> extends Cloneable {
//同步执行网络请求
Response<T> execute() throws IOException;
//异步执行网络请求
void enqueue(Callback<T> callback);
//是否已经执行过请求
boolean isExecuted();
//取消请求
void cancel();
//请求是否已经取消
boolean isCanceled();
//复制一个请求
Call<T> clone();
//返回okhttp3.Request
Request request();
}
因为Retrofit
没有实现具体的网络请求,默认是使用OkHttp
请求网络的,OkHttpCall
内部是调用OkHttp
的Call
去请求网络得到请求结果okhttp3.Response
,再进一步包装成Retrofit
的Response
,如果是异步请求的话,再把okhttp3.Callback
回调包装成Retrofit
的Callback
;
从OkHttpCall<T>
的构造函数可以看出它的泛型是由它构造函数里的参数ServiceMethod<T>
决定的,而后者的泛型又和它的成员变量private final Converter<ResponseBody, T> responseConverter;
所对应的,而这个成员变量的赋值是发生在上文所提到的ServiceMethod.Builder
的build()
方法里responseConverter = createResponseConverter();
以下是createResponseConverter()
函数的源码:
private Converter<ResponseBody, T> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create converter for %s", responseType);
}
}
而retrofit.responseBodyConverter()
最终又调用了Retrofit
的nextResponseBodyConverter
:
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
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;
}
}
/...部分代码略过.../
}
以上代码就是遍历了一个converterFactories
集合,如果符合条件就返回该Converter.Factory
工厂生成的Converter<ResponseBody, T>
,最后遍历完也找不到合适的转换器Converter
就抛出异常,再看一下Converter
是怎么定义的:
public interface Converter<F, T> {
/**
* 把对象F转换成T对象返回;
*/
T convert(F value) throws IOException;
/** Creates {@link Converter} instances based on a type and target usage. */
abstract class Factory {
/**
* 返回一个retrofit2.Converter对象, 这个对象可以把HTTP的ResponseBody转换成type参数所对应的类型,
* 如果该工厂不能处理则返回空。这个方法用于创建响应类型的转换器, 类似Call<SimpleResponse>这样的申明,
* 负责把ResponseBody类型转换成SimpleResponse类型;
*/
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
/**
* 返回一个retrofit2.Converter对象, 这个对象可以把type参数所对应的类型转换成HTTP的RequestBody,
* 如果该工厂不能处理则返回空。这个方法用于创建可以把Body、Part、PartMap等注解对应的值转换成RequestBody类型的转换器
*/
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
/**
* 返回一个retrofit2.Converter对象, 这个对象可以把type参数所对应的类型转换成String,
* 如果该工厂不能处理则返回空。这个方法用于创建可以把Field、FieldMap、Header、HeaderMap、Path、
* Query、QueryMap等注解对应的值转换成String类型的转换器
*/
public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
}
}
那么这个ServiceMethod
在得到匹配的responseConverter
后肯定是在OkHttp
请求成功后把它的结果ResponseBody
交给转换器转换成相应的类型,怎么请求网络呢,看下OkHttpCall
的enqueue()
和execute()
方法:
final class OkHttpCall<T> implements Call<T> {
private okhttp3.Call rawCall;
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
/...略过.../
call = rawCall = createRawCall();
/...略过.../
call.enqueue(new okhttp3.Callback() {/...略过.../});
}
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
/...略过.../
call = rawCall = createRawCall();
/...略过.../
//把Http的请求的结果转成对应类型
return parseResponse(call.execute());
}
private okhttp3.Call createRawCall() throws IOException {
//通过serviceMethod配合接口方法的参数args得到OkHttp的Request;
Request request = serviceMethod.toRequest(args);
//serviceMethod的callFactory是由retrofit提供的,默认是OkHttpClient
//这个call工厂负责把Request生成一个OkHttp的Call,用于操控网络请求
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
/...略过.../
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;
}
}
/...其余代码略过.../
}
先是执行createRawCall()
生成Okhttp
的Call
,在OkHttp
的网络请求执行完后,通过parseResponse()
方法转换成对应类型,里面实际是调用了serviceMethod.toResponse()
方法进行转换的:
/** Builds a method return value from an HTTP response body. */
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
正是在实例化ServiceMethod
的时候得到合适的转换器,每次请求成功后调用转换器的convert()
方法把ResponseBody转换成对应的类型;
既然我们可以正常使用接口,那么肯定是找到了合适的转换器Converter
,而转换器又是由它的工厂集合遍历得到的,这个工厂集合converterFactories
是Retrofit
的成员变量,在Retrofit.Builder
类的构造函数初始化的:
public static final class Builder {
private List<Converter.Factory> converterFactories = new ArrayList<>();
Builder(Platform platform) {
this.platform = platform;
// 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());
}
public Builder() {
this(Platform.get());
}
/...其余代码略过.../
}
可以看到已经提供了一个默认的转换器工厂实现类BuiltInConverters
:
final class BuiltInConverters extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (type == ResponseBody.class) {
if (Utils.isAnnotationPresent(annotations, Streaming.class)) {
return StreamingResponseBodyConverter.INSTANCE;
}
return BufferingResponseBodyConverter.INSTANCE;
}
if (type == Void.class) {
return VoidResponseBodyConverter.INSTANCE;
}
return null;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
return RequestBodyConverter.INSTANCE;
}
return null;
}
@Override public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (type == String.class) {
return StringConverter.INSTANCE;
}
return null;
}
}
BuiltInConverters
的responseBodyConverter
方法只支持RequestBody
和Void
的类型转换,也就是说,我们在申明接口方法时,默认是只支持Call<RequestBody>
和Call<Void>
这样的返回值;可是我们上文申明的却是Call<GitHubUser>
,这是因为上文我们在实例化Retrofit
的时候通过addConverterFactory(GsonConverterFactory.create())
方法给Retrofit
的转换器工厂集合增加了一个Gson转换器工厂,这个工厂会根据接口申明方法里返回值的泛型对应的Type
生成一个GsonResponseBodyConverter
转换器,把RequestBody
类型转换成了GitHubUser
类型:
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();
}
}
}
那么在上文创建的InvocationHandler
类里的invoke()
方法里所得到的OkHttpCall<T>
实际上已经符合我们在接口方法里申明的返回值,像OkHttpCall<GitHubUser>
这样,为什么在invoke()
最后不直接返回OkHttpCall<GitHubUser>
,而是调用return serviceMethod.callAdapter.adapt(okHttpCall);
呢?先看一下adapt()
方法的申明:
public interface CallAdapter<T> {
//将一个Call<R>实例转换成T;
<R> T adapt(Call<R> call);
/...其余代码略过.../
}
这个方法跟上文里的提到的转换器Converter
很相似,不过这里是限定了必须把Call<R>
转换成其他类型返回;
在Android里必须在主线程更新UI,而OkHttpCall
执行回调的时候没有进行线程切换,不同平台的默认实现也不一样,在Android平台里默认的CallAdapterFactory
生成的CallAdapter
的adapt(okHttpCall)
方法返回了一个ExecutorCallbackCall
,也是继承了Retrofit
的Call
接口:
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;
}
/...其余代码略过.../
}
实际上在实现Call
接口的方法时,都是用成员变量delegate
,也就是okHttpCall
去实现,唯一不同的是在执行异步请求enqueue()
方法时,在执行回调是是通过callbackExecutor
去执行,而在Android平台里的默认的callbackExecutor
对应是上文提到的MainThreadExecutor
,通过Handler
在主线程执行回调,也可以在实例化Retrofit
的时候使用它的Builder
的callbackExecutor(executor)
方法使用自定义的Executor;然而callAdapter.adapt(okHttpCall)
的作用还不仅仅是为了方便在主线程执行异步请求的回调,因为我们可以在实例化Retrofit
的时候调用addCallAdapterFactory()
方法增加自己的CallAdapter
工厂,把Call<R>
类型转换成你想申明的任何类型,比较典型的应用场景是结合RxJava
,这样我们就可以在接口申明方法时这样申明:
public interface TestService {
@GET("users/yuhengye")
Observable<GitHubUser> getUserInfo();
}
然后这样请求网络:
RetrofitManager
.getTestService()
.getUserInfo()//返回一个Observable<GitHubUser>对象
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GitHubUser>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(GitHubUser result) {
}
});
通过返回Observable<GitHubUser>
对象请求网络后需要自己操纵线程的切换执行回调,对比一下文章开始用Call<GitHubUser>
返回值去请求网络并没有什么优势,但是用RxJava
可以很优雅、方便地实现合并多个网络请求最后统一执行回调、一条网络请求完成后紧接着执行下一条网络请求等需求,如果用最原始的Call<GitHubUser>
去实现这些需求则要自己实现,并且可能会引起多重嵌套,有关Retrofit
配合RxJava
的使用后面会另外再写一篇文章;
上文已经分析了Retrofit
的实现原理,现在我们再总结一下Retrofit
的构造函数参数所代表的含义:
callFactory
: 该工厂负责生成一个OkHttp
的Call
,用于请求网络,一般是使用OkHttpClient
;
baseUrl
: 接口前缀地址
converterFactories
: 转换工厂集合,负责把HTTP请求的参数(传给接口方法的参数)转换成RequestBody
或者String,以及把HTTP请求成功后的结果ResponseBody
转换成接口方法申明的对应泛型类型;
adapterFactories
:适配器工厂集合,负责把HTTP请求成功后的Call(实际上就是OkHttpCall)转换成自定义的类型;
callbackExecutor
:当接口方法申明的返回值类型是Call<T>这种类型时,由默认提供的平台适配器转换,并且在执行异步请求回调时使用callbackExecutor去执行回调,主要是为了方便线程切换;
validateEagerly
:如果该值为true,在使用Retrofit
的create()
方法创建接口的实例时,会生成所有的ServiceMethod
,如果为false,则在该接口方法第一次请求时才去创建;
以上就是Retrofit
的主要实现源码分析;
最后总结一下在Retrofit
里调用我们申明接口方法的主要流程:
检查是否有该接口方法对应的ServiceMethod
缓存,没有则生成一个加入缓存,并且解析接口方法的注解,以及方法参数的注解,生成对应的参数处理器用于处理我们传给方法的参数;以及创建合适的responseConverter
,它可以把网络请求结果ResponseBody
转换成该接口方法返回值所需要的类型,这个接口方法返回值所需要的类型又是由CallAdapter
提供的;默认的CallAdapter
是这样的处理的:如果接口方法返回类型是Call<String>
,则这里所需要的类型是String
;这样在调用接口方法的时候,实际是先生成一个OkHttpCall<String>
,这个对象再经由合适的CallAdapter
的adapt()
方法转换成在接口方法里所申明的返回值类型;
网友评论