Retrofit2.3是如何解析在介面類中定義的方法?
前言
Retrofit的核心在於它的create 方法中使用了動態代理,在這裡面主要是loadServiceMethod方法:
以下程式碼基於Retrofit2.5.0 (跟2.3.0 程式碼存在明顯不同)
public <T> T create(final Class<T> service) { //省略無關程式碼 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { //省略無關程式碼 return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); }
請求方法的解析
首先來看loadServiceMethod 方法:
Retrofit.loadServiceMethod(Method method)
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>(); 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; }
serviceMethodCache是一個快取的Map,這個方法主要就是執行了ServiceMethod.parseAnnotations
先看看返回的類ServiceMethod
這個方法返回ServiceMethod 這個類:
abstract class ServiceMethod<T> {//抽象類 static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {//解析註解 RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);//請求工廠初始化 Type returnType = method.getGenericReturnType();//獲取方法的返回型別 //省略無關程式碼 return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);//返回解析結果 } abstract T invoke(Object[] args); }
繼續看RequestFactory.parseAnnotations(retrofit, method):
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { return new Builder(retrofit, method).build(); }
在這裡又用到了建立者方法來建立這個請求工廠,進入Builder:
Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit;//當前的retrofit例項 this.method = method;//請求的方法 this.methodAnnotations = method.getAnnotations();//註解 this.parameterTypes = method.getGenericParameterTypes();//引數的型別集合 this.parameterAnnotationsArray = method.getParameterAnnotations();//引數的註解集合 }
可以看到這個建立類裡面包含了我們在介面定義的方法的所有資訊,包括註解和引數,再繼續來看它的build 方法做了什麼:
RequestFactory build() { for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation);//重點1.解析方法上面的註解 } //省略無關程式碼 int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);//重點2.解析方法的引數 } //省略無關程式碼 return new RequestFactory(this); }
在這裡,進行了對方法的具體解析,主要是兩個步驟
- 解析方法上面的註解parseMethodAnnotation
- 解析方法的請求引數註解parseParameter
到兩個方法裡面看看:
方法上面的註解
RequestFactory.parseMethodAnnotation
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; } }
在這裡,看到了熟悉的一幕,我們平常使用Retrofit時在方法上面使用的@POST和@GET之類的註解,就是在這個方法裡面進行的解析,這裡先做一個判斷,繼續呼叫parseHttpMethodAndPath
RequestFactory.parseHttpMethodAndPath
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);//解析請求連結裡面的引數 }
在這裡進行了關於請求的方法等相關屬性的賦值,最後呼叫parsePathParameters 解析我們在註解裡面傳入的地址的引數:
RequestFactory.parsePathParameters
private static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*"; private static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}"); static Set<String> parsePathParameters(String path) { Matcher m = PARAM_URL_REGEX.matcher(path); Set<String> patterns = new LinkedHashSet<>(); while (m.find()) { patterns.add(m.group(1)); } return patterns; }
這裡應該都能看懂,使用正則表示式來匹配。
到這裡,完成了對方法上面的註解的解析,接下來,進行對方法的引數的解析:
方法的請求引數註解
RequestFactory.parseParameter
private ParameterHandler<?> parseParameter( int p, Type parameterType, @Nullable Annotation[] annotations) { ParameterHandler<?> result = null; if (annotations != null) { for (Annotation annotation : annotations) { ParameterHandler<?> annotationAction = parseParameterAnnotation(p, parameterType, annotations, annotation);//引數解析 //省略無關程式碼 result = annotationAction; } } //省略無關程式碼 return result; }
返回的ParameterHandler 類為引數的控制代碼,這裡是一個抽象類,裡面有Header ,Path ,Query 等跟我們在引數前面加的標註同名的實現類。
使用一個迴圈,來解析每個引數的註解,這裡呼叫了parseParameterAnnotation方法,這個方法跟剛才解析方法上面的註解parseMethodAnnotation很像,裡面進行了很多判斷,在引數裡面可以加的註解很多,所以方法太長,這裡看一看我們經常用到的GET請求的@Query註解的解析:
RequestFactory.parseParameterAnnotation
//省略無關程式碼 } else if (annotation instanceof Query) { validateResolvableType(p, type); Query query = (Query) annotation; String name = query.value(); boolean encoded = query.encoded(); Class<?> rawParameterType = Utils.getRawType(type);//獲取型別 gotQuery = true; if (Iterable.class.isAssignableFrom(rawParameterType)) {//是否是集合 if (!(type instanceof ParameterizedType)) { throw parameterError(method, 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.Query<>(name, converter, encoded).iterable(); } else if (rawParameterType.isArray()) {//是否是集合的陣列 Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations); return new ParameterHandler.Query<>(name, converter, encoded).array(); } else {//普通型別 Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Query<>(name, converter, encoded); } } else if (annotation instanceof QueryName) { //省略無關程式碼
在這個方法裡面進行了型別和泛型相關的判斷,裡面都呼叫了retrofit.stringConverter方法:
retrofit.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; }
這個方法的作用是獲取請求引數的json解析器,一個迴圈從converterFactories陣列中依次獲取加入的解析器工廠,我們在之前建立Retrofit傳入的是GsonConverterFactory ,開啟這個類,並沒有發現stringConverter 方法,再開啟它的父類Converter.Factory ,看到了這個方法:
public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return null; }
返回的是空,說明這裡不需要使用解析工廠將請求的引數轉化為String。所以直接呼叫最後一句
return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
static final class ToStringConverter implements Converter<Object, String> { static final ToStringConverter INSTANCE = new ToStringConverter(); @Override public String convert(Object value) { return value.toString(); } }
返回的是一個預設的轉化類,在這個類的convert方法使用的是類自身的toString 來轉化。
到此為止,就完成了我們在介面中定義的那個方法的全部解析。
總結
Retrofit使用了動態代理,所以每次執行我們在介面中定義的方法會來到動態代理中的invoke方法,在這裡面,又執行了loadServiceMethod來實現對方法的解析,主要是兩個步驟:
- 解析方法上面的註解(如@Headers,@POST,@GET)
- 解析方法的請求引數前面的註解(如@Query, @Field)