Retrofit2源码解析——动态代理和注解的实战应用!
本想年前搞定这篇文章,接下来进入面试题的整理。但是年前确实有些心不在焉,拖拖拉拉就到了现在才完成。唉,这该死的拖延症~
前面讲到了代理和注解的基本应用,感觉有一点枯燥无味。空洞的理论脱离了实际应用就会变得毫无意义。So,今天要写的就是将动态代理和注解完美应用的Android网络请求框架的分析——Retrofit2。
关于OkHttp
稍微了解Retrofit
的就会知道Retrofit
也是使用的OkHttp
作为请求框架,Retrofit
只是对其进行封装,更加方便使用。我们看下OkHttp
的请求方法:
OkHttpClient client = new OkHttpClient();
//创建一个Request
Request request = new Request.Builder()
.get()
.url(url)
.build();
//通过client发起请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
// String str = response.body().string();
}
}
});
这是关于OkHttp的简单使用,这里面执行了一个get请求。前面也说了Retrofit是对OkHttp的封装,那么Retrofit最后也会调用这些方法。不信?咱往下看。
1. Retrofit的创建
Retrofit通过Builder来创建,下面是简单创建:
new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) // 添加Gosn转换器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 添加RxJava2支持
.build();
来看下build()
过程:
public Builder() {
// 获取当前的平台,我们这里是Android
this(Platform.get());
}
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 Retrofit build() {
// 检查baseUrl,这里并没有对""这种情况判断
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 如果没有传入OkHttpClient,设置默认的OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 也是默认的执行器,根据平台来的,这里的是Android的平台
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
// MainThreadExecutor
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
// 添加Call adapter,这个使用RxJava的时候会添加
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(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
@Nullable Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
// 这个是通过Collections.unmodifiableList创建一个只可读的List
this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
this.callbackExecutor = callbackExecutor;
// 默认为false
this.validateEagerly = validateEagerly;
}
2. 动态代理的使用
OK,创建完成。接着我们需要写一个接口:
public interface Api {
@GET
Call<ResponseBody> get(@Url String url, @QueryMap Map<String, String> params);
}
// 接口创建
Api api = retrofit.create(Api.class)
使用我们刚刚创建的Retrofit
来创建我们的Api
,会调用Retrofit
的create
方法:
Retrofit.java:
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.
// 该方法是在Object中定义,直接调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 这里返回值一直为false
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);
// 返回的值为ExecutorCallbackCall,后面会写
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
创建我们的Api
使用了动态代理,如果方法是Object
中定义的,那么直接调用该方法;否则,需要将该方法所用到的注解值提取:
Retrofit.java:
// 加载方法放入缓存中
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.java:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// 方法的注解
this.methodAnnotations = method.getAnnotations();
// 方法参数类型
this.parameterTypes = method.getGenericParameterTypes();
// 方法参数注解
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
public ServiceMethod build() {
// 创建CallAdapter
callAdapter = createCallAdapter();
// 获得返回值类型
responseType = callAdapter.responseType();
// 返回值类型不能为Response或者okhttp3.Response
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);
}
//注解使用错误的抛出异常
......
// 参数长度(参数注解的长度)
int parameterCount = parameterAnnotationsArray.length;
// 创建ParameterHandler数组,该类是个抽象类,主要用于将参数对应的值获取,并设置到RequestBuilder中
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.");
}
// 根据注解创建对应的ParameterHandler的子类
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
//注解使用错误的抛出异常
......
return new ServiceMethod<>(this);
}
// 创建CallAdapter
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);
}
// 返回值为void
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
// 调用retrofit的callAdapter方法,我们在构建Retrofit是如果没有传入自定义的CallAdapter,那么则使用当前平台的CallAdapter
// 这里返回的是Android平台默认的CallAdapter(通过ExecutorCallAdapterFactory.get(参数)获得)
// new CallAdapter<Object, Call<?>>() {
// @Override public Type responseType() {
// return responseType;
// }
//
// @Override public Call<Object> adapt(Call<Object> call) {
// return new ExecutorCallbackCall<>(callbackExecutor, call);
// }
//}
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);
}
}
上面我们看到了关于ServiceMethod
对象的创建,其中parseMethodAnnotation()
方法是对将方法的注解提取并对ServiceMetohd
的成员变量赋值,主要用于对http的请求方式设置、url以及注解正确性的检查,parseParameter()
方法用于生成一个ParameterHandler
类的子类,该子类需要实现apply()
方法,用于将注解中以及参数的值设置到请求参数中,这里是后话了。先看下parseMethodAnnotation()
方法:
//方法中主要调用了parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);这个方法,只是参数不同。看下该方法实现:
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);
}
// 设置请求方法,以GET为例
this.httpMethod = httpMethod;
// get方法没有body
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("URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
// 相对rul设置
this.relativeUrl = value;
// 将{}作为相对url的参数放入Set中
this.relativeUrlParamNames = parsePathParameters(value);
}
上面的方法主要是对Method的注解进行处理,通过注解来获得该方法的请求方式以及url。接着我们来看下parseParameter()
方法:
private ParameterHandler<?> parseParameter(
int p, Type parameterType, Annotation[] annotations) {
ParameterHandler<?> result = null;
// 遍历参数注解
for (Annotation annotation : annotations) {
// 根据注解类型真正创建ParameterHandler对应的子类
ParameterHandler<?> annotationAction = parseParameterAnnotation(
p, parameterType, annotations, annotation);
if (annotationAction == null) {
continue;
}
if (result != null) { // 只会创建一次,如果已经创建了,那么抛出异常
throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
}
result = annotationAction;
}
// result为null,说明该参数没有被注解修饰,抛出异常
if (result == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
return result;
}
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
if (annotation instanceof Url) {
......
} else if (annotation instanceof Path) {
......
} else if (annotation instanceof Query) {
......
} else if (annotation instanceof QueryName) {
......
} else if (annotation instanceof QueryMap) { //我们比较常见的@QueryMap
Class<?> rawParameterType = Utils.getRawType(type);
if (!Map.class.isAssignableFrom(rawParameterType)) {
throw parameterError(p, "@QueryMap parameter type must be Map.");
}
Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
if (!(mapType instanceof ParameterizedType)) {
throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)");
}
ParameterizedType parameterizedType = (ParameterizedType) mapType;
Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
if (String.class != keyType) {
throw parameterError(p, "@QueryMap keys must be of type String: " + keyType);
}
Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
// 这段代码直接返回了(Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE,
// 最终是ToStringConverter对象
Converter<?, String> valueConverter =
retrofit.stringConverter(valueType, annotations);
// 创建了QueryMap对象,QueryMap是继承ParameterHandler,并且实现了其抽象方法apply
return new ParameterHandler.QueryMap<>(valueConverter, ((QueryMap) annotation).encoded());
} else if (annotation instanceof Header) {
......
} else if (annotation instanceof HeaderMap) {
......
} else if (annotation instanceof Field) {
......
} else if (annotation instanceof FieldMap) {
......
} else if (annotation instanceof Part) {
......
} else if (annotation instanceof PartMap) {
......
} else if (annotation instanceof Body) {
......
}
return null; // Not a Retrofit annotation.
}
通过遍历参数和该参数的注解可以生成ParameterHandler
的子类,用于对注解的类型的存储。当最后需要组装参数时,只需要遍历ParameterHandler
数组并且调用其apply
方法即可。
3. 调用OkHttp的过程
写到这里差点忘记到底该写哪里了。。往上翻一翻就可以知道,我们才写完loadServiceMethod
这个方法,继续往下看OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
,这里创建了一个OkHttpCall
的对象,将我们刚才创建的ServiceMethod
和执行该方法的参数全部传入。最后调用return serviceMethod.callAdapter.adapt(okHttpCall);
返回对应的值(我们这里是Call<ResponseBody>
)。来看下callAdapter.adapt(okHttpCall)
方法:
创建过程讲过,我们会有一个默认的Executor和CallAdapter.Factory,这两个类在Android平台分别为Platform.MainThreadExecutor和ExecutorCallAdapterFactory。
ExecutorCallAdapterFactory.java:
// 将Executor传入
ExecutorCallAdapterFactory(Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
// 上面已经说了,这里功过get方法获得CallAdapter对象,并且调用其adapt方法,返回Call<Object>对象
@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);
}
};
}
现在我们通过我们的接口Call<ResponseBody> call = api.get(xxx,xxx)
的方式构建出一个Call对象,通过我们上面的分析,这里返回的为new ExecutorCallAdapterFactory.ExecutorCallbackCall<>(callbackExecutor, call)
,当我们请求接口时,通常是这样call.enqueue()
,来看下实现原理吧:
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
// 执行调用了传入的call对象的enqueue方法
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
// Android平台这里是通过Handler来进行执行
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, t);
}
});
}
});
}
// 这里面的callbackExecutor就是该Executor
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
通过构造方法以及enqueue
方法可以知道,最终的方法是调用了传入的Call对象的enqueue
方法。这个传入的对象又是谁呢?就是上面写到的OkHttpCall
,OkHttpCall
构造需要将我们创建的ServiceMethod
和方法执行的参数传入。其enqueue()
如下:
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
// 这里就是okhttp3的Call
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对象为null,则需要创建
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
// 创建失败了。。。
if (failure != null) {
callback.onFailure(this, failure);
return;
}
// 取消
if (canceled) {
call.cancel();
}
// 执行,传入了okhttp的回调,在其中调用Retrofi的回调
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
OK,现在已经可以看到调用了okhttp
的Call
的enqueue
方法,那么我们的参数呢?这里是怎么设置的?我们看下createRawCall()
方法:
OkHttpCall.java:
private okhttp3.Call createRawCall() throws IOException {
// 又调用了serviceMethod的toRequest方法,将参数传入
Request request = serviceMethod.toRequest(args);
// 生成新的Call callFactory为OkhttpClient
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
ServiceMethod.java:
Request toRequest(@Nullable Object... args) throws IOException {
// 穿件RequestBuilder对象
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子类
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
// 参数长度和ParameterHandler的长度一致
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
// 遍历ParameterHandler数组,分别调用apply方法对requestBuilder进行组装
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
RequestBuilder.java:
// build方法是创建一个Request对象
Request build() {
HttpUrl url;
HttpUrl.Builder urlBuilder = this.urlBuilder;
if (urlBuilder != null) {
url = urlBuilder.build();
} else {
// No query parameters triggered builder creation, just combine the relative URL and base URL.
//noinspection ConstantConditions Non-null if urlBuilder is null.
url = baseUrl.resolve(relativeUrl);
if (url == null) {
throw new IllegalArgumentException(
"Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
}
}
// 根据是否有form或者multipartBuilder来判断请求Body部分如何创建
RequestBody body = this.body;
if (body == null) {
// Try to pull from one of the builders.
if (formBuilder != null) {
body = formBuilder.build();
} else if (multipartBuilder != null) {
body = multipartBuilder.build();
} else if (hasBody) {
// Body is absent, make an empty body.
body = RequestBody.create(null, new byte[0]);
}
}
MediaType contentType = this.contentType;
if (contentType != null) {
if (body != null) {
body = new RequestBuilder.ContentTypeOverridingRequestBody(body, contentType);
} else {
requestBuilder.addHeader("Content-Type", contentType.toString());
}
}
// 调用Request的build方法创建Request对象
return requestBuilder
.url(url)
.method(method, body)
.build();
}
写到这里我们往回看,先通过Request.Builder
创建Request
对象,接着使用OkHttpClient
创建Call
对象,最后通过call.enqueue(callback)
请求接口,并将数据通过callback
进行成功或失败的回调。
和我们最开始写的OkHttp请求接口方式一毛一样!!
没有总结
源码的话可能要暂停一段时间了,这段时间需要整理下面试题。希望能在今年卖个好价钱吧,哈哈。