前言
之前我们已经复习了Retrofit的基础用法,Retrofit的源码理解并不复杂,他实现的主要功能就是把接口文件通过注解转化成Okhttp请求,所以我们弄懂了主线,整个Retrofit我们就明白了。
正文
首先复习一下Retrofit的用法:
val retrofit = Retrofit.Builder() // 必填项 .baseUrl("http://www.baidu.com") .client(OkHttpClient()) // 对得到的结果进行转换,常用的有加密解密,json转换等等 .addConverterFactory(StringConvertFactory()) // 对返回的结果进行封装,常用的有之间转化成Rxjava对象 // 这里我们简单的进行包装 .addCallAdapterFactory(ResponseWrapperCallAdapterFactory()) .build() api = retrofit.create(TestApi::class.java)
Retrofit的通过构建者模式创建,这里的重点是:
api = retrofit.create(TestApi::class.java)
我们看看这个create方法是怎么解析接口文件的:
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); } // 如果是default方法,根据平台类型直接调用 // android平台固定返回false if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } // 通过Method,封装成okhttp请求 ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); // 发起网络请求 return serviceMethod.adapt(okHttpCall); } }); }
通过create方法,创建了指定接口文件的动态代理,每次我们通过动态代理访问对应的方法的时候,都会进入到invoke方法,通过invoke方法,我们可以获得这个方法的注解信息,然后转换成okhttp请求。
首先检查了接口文件是否合法,判断是否是interface接口并且方法书大于0。这里我们还看到了构建Retrofit时设置的validateEagerly属性,如果是true,会在创建接口代理时,解析所有的方法,所以我们一般不会开启这个设置。eagerlyValidateMethods方法内部调用的是loadServiceMethod方法,loadServiceMethod方法解析调用方法的注解信息,封装成ServiceMethod对象。
ServiceMethod<?, ?> loadServiceMethod(Method method) { // 如果缓存中已经有了这个方法,直接返回 ServiceMethod<?, ?> result = serviceMethodCache.get(method); if (result != null) return result; // 同步锁 synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { // 把这个方法封装成ServiceMethod对象,保存起来 result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); } } return result; }
首先检查缓存,如果缓存了这个方法的请求信息,直接返回,否则会重新解析一次,解析的步骤被封装到ServiceMethod.Builder中,所以我们看一下ServiceMethod.Builder的build方法。
public ServiceMethod build() { // 创建callAdapter 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 responseConverter = createResponseConverter(); // 解析注解 for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } // 检查是否设置了请求方式 if (httpMethod == null) { throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.)."); } // 如果没有添加body,要检查请求的方式是否支持无body请求 if (!hasBody) { if (isMultipart) { throw methodError( "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); } if (isFormEncoded) { throw methodError("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(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."); } // 解析参数的注解 parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } if (relativeUrl == null && !gotUrl) { throw methodError("Missing either @%s URL or @Url parameter.", httpMethod); } if (!isFormEncoded && !isMultipart && !hasBody && gotBody) { throw methodError("Non-body HTTP method cannot contain @Body."); } if (isFormEncoded && !gotField) { throw methodError("Form-encoded method must contain at least one @Field."); } if (isMultipart && !gotPart) { throw methodError("Multipart method must contain at least one @Part."); } return new ServiceMethod<>(this); }
在build方法中,开始了对注解的解析,这个还做了很多的判断,例如是否设置了url的检查,某些注解使用冲突的问题等等。这个方法中主要看parseMethodAnnotation方法和parseParameter方法。
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); if (!Void.class.equals(responseType)) { throw methodError("HEAD method must use Void as response type."); } } 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("@Headers annotation is empty."); } headers = parseHeaders(headersToParse); } else if (annotation instanceof Multipart) { if (isFormEncoded) { throw methodError("Only one encoding annotation is allowed."); } isMultipart = true; } else if (annotation instanceof FormUrlEncoded) { if (isMultipart) { throw methodError("Only one encoding annotation is allowed."); } isFormEncoded = true; } }
这个分别解析了方法的注解,我们看到了熟悉@GET、@POST等注解,具体的实现在parseHttpMethodAndPath中:
/** * 解析方法中注解,保存对应的value * */ 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. // 判断url链接之后的参数是否合法 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; // 从地址中分离出设置的参数 this.relativeUrlParamNames = parsePathParameters(value); }
parseHttpMethodAndPath先获取注解中网络请求的相对路径,如果设置了相对地址,最后会和baseUrl拼接成完成的请求地址,另外还检查了在相对地址中,是不允许设置参数的,考虑到有些对url链接的地址组成不了解朋友,我们举一个简单的例子:
如果想要在网址中添加参数,需要在末尾加上?,然后通过 key=value&key=value的方式拼接
共同學習,寫下你的評論
評論加載中...
作者其他優質文章