前言
之前我们已经复习了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的方式拼接
具体的url了解的格式大家可以自己查看相关资料。看完了解析方法上的直接,接着看解析方法中参数的注解,因为代码实在是太多了,我们就挑其中一个熟悉的注解作为理解的例子:
else if (annotation instanceof Field) { if (!isFormEncoded) { throw parameterError(p, "@Field parameters can only be used with form encoding."); } Field field = (Field) annotation; String name = field.value(); boolean encoded = field.encoded(); gotField = true; Class<?> rawParameterType = Utils.getRawType(type); // 如果是集合,需要指定泛型是String if (Iterable.class.isAssignableFrom(rawParameterType)) { if (!(type instanceof ParameterizedType)) { throw parameterError(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.Field<>(name, converter, encoded).iterable(); } // 数组 else if (rawParameterType.isArray()) { Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations); return new ParameterHandler.Field<>(name, converter, encoded).array(); } // 其他 else { Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Field<>(name, converter, encoded); } }
我们看到@Field注解必须和@FormUrlEncoded一起使用,把对应的注解的类型的key和设置的参数值加入到表单中去。
现在方法的注解和参数的注解都已经得到了,Retrofit把ServiceMethod和指定的参数封装成OKhttpCall:
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
OkhttpCall是ServiceMethod和okhttp请求转换的处理类,他主要是负责成request和处理response,这里贴出几个重要的方法:
生成okhttp的网络请求:
/** * OKhttpCall.toCall实际上调用的ServiceMethod.toCall方法 * 生成okhttp网络请求 */ okhttp3.Call toCall(@Nullable Object... args) throws IOException { // 封装网络请求的信息 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<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; // 判断参数的数量是否和方法的数量相等 int argumentCount = args != null ? args.length : 0; if (argumentCount != handlers.length) { throw new IllegalArgumentException("Argument count (" + argumentCount + ") doesn't match expected count (" + handlers.length + ")"); } // 把参数添加到requestBuilder中 for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); } // 调用okhttpClient创建Call对象 return callFactory.newCall(requestBuilder.build()); }
获取okhttp的request信息:
@Override public synchronized Request request() { // 如果已经有okhttp的call请求,返回call的request okhttp3.Call call = rawCall; if (call != null) { return call.request(); } // 判断是否创建请求失败过 if (creationFailure != null) { if (creationFailure instanceof IOException) { throw new RuntimeException("Unable to create request.", creationFailure); } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } try { // 创建okhttp请求 return (rawCall = createRawCall()).request(); } catch (RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. creationFailure = e; throw e; } catch (IOException e) { creationFailure = e; throw new RuntimeException("Unable to create request.", e); } }
解析okhttp的请求结果:
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { if (creationFailure instanceof IOException) { throw (IOException) creationFailure; } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } call = rawCall; if (call == null) { try { call = rawCall = createRawCall(); } catch (IOException | RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. creationFailure = e; throw e; } } } if (canceled) { call.cancel(); } return parseResponse(call.execute()); }
其中在解析网络请求结果的过程中,调用了我们设置的ConvertFactory,对得到的响应结果进行转换:
/** Builds a method return value from an HTTP response body. */ /** * 对得到的网络请求结果进行转换 * */ R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
到此为止Retrofit的发起网络请求的整个流程就结束了。
哎?是不是忘了点什么?没错那就是CallAdapter,其实CallAdapter只是对网络请求结果的包装,也就对ConvertFactory的转换结果的封装,回顾一下我们自定义的CallAdapterFactory:
/** * Created by li.zhipeng on 2018/8/29. * * 把得到网络请求结果String,转换成ResponseWrapper */public class ResponseWrapperCallAdapterFactory extends CallAdapter.Factory { @Override public CallAdapter<String, ResponseWrapper> get(@NonNull final Type returnType, @NonNull Annotation[] annotations, @NonNull Retrofit retrofit) { return new CallAdapter<String, ResponseWrapper>() { @Override public Type responseType() { return returnType; } @Override public ResponseWrapper adapt(@NonNull Call<String> call) { try { return new ResponseWrapper(call.execute().body()); } catch (IOException e) { e.printStackTrace(); } return new ResponseWrapper("error"); } }; } }
这个类的重点就是:
@Override public ResponseWrapper adapt(@NonNull Call<String> call) { try { return new ResponseWrapper(call.execute().body()); } catch (IOException e) { e.printStackTrace(); } return new ResponseWrapper("error"); }
我们在CallAdapter中开启了网络请求,并且把得到的结果封装成我们需要的对象。在动态代理中
serviceMethod.adapt(okHttpCall);
T adapt(Call<R> call) { return callAdapter.adapt(call); }
通过ServiceMethod间接调用了我们设置的CallAdapter的adapt方法。
作者:珠穆朗玛小王子
链接:https://www.jianshu.com/p/fd02cf568fb0
共同学习,写下你的评论
评论加载中...
作者其他优质文章