准备知识
我眼中的Java-Type体系(2)对Method解析的时候要用到
在分析源码前有哪些疑问呢?
CallAdapter是什么?
Converter是什么?
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
保存Converter为什么是一个List?
private List<Converter.Factory> converterFactories = new ArrayList<>();
保存CallAdapter为什么也是一个List?
无论你是不是对上面有疑问,在看完之后相信你都会有答案了。
总结
- Retrofit是OKHttp辅助工具
- Retrofit本身是一个注解解析器
- Retrofit可以和RxJava更容易配合使用
- ServiceMethod 的主要作用是解析构建Request 和Response的解析
- OkHttpCall 主要作用是实现OKhttp3的调用
- CallAdapter 作用Retrofit.Call命令器的转换成自定义的命令器
- Converter<?, RequestBody> requestBodyConverter是实现Request自定义的类转换成RequestBody
- Converter<ResponseBody, ?> responseBodyConverter是将ResponseBody转换成我们自定义的类
在对源码进行分析之前先看一下Retrofit,类是很少的。
我们分析Retrofit2,带着设计者实现功能的想法去分析。下面是对1到4点的说明
- 构建OKHttpClient 帮我们实现Http请求
- 构建Retrofit,保存相关的参数(参数作用后面会提及)
- 实现网络请求
- 构建Http请求的传递参数和返回参数
在分析Retrofit前我们看一下Okhttp请求
大概的步骤是
1创建一个OKHTTPClient(配置连接时间,读写时间,超时等参数)
2创建一个Request(配置url,head,get,post方法,body等)
3发送请求获得response
4 对response进行解析
这也是一个HTTP请求的过程。
正式开始我们分析的旅程
Retrofit提供一些辅助类让我们更加方便的
我们现在有一个像www.ss.com/xiMing/tt.php发送一个Student ,返回一个Person的需求。(假设我们用Json格式数据传输)如果我们用OKHTTP的方式,我们要把Student转换成json格式,在服务器返回的时候又要把json格式转换成Person。如果我们有多次这样的请求,我们每个都要这样做。Retrofit把这个过程给我们处理了。而之前Retrofit做的,我们不需要关注的,就是我们今天要学习的。
对 @POST("{user}/tt.php")Call<Person> postStudent(@Body Student student,@Path("user") String user);解析最核心的类是ServiceMethod类。
create方法是创建RequestService的实现。创建一个interface的实现,要么用生成工具,要么用反射。作者使用了后者。Proxy.newProxyInstance反射实现了RequestService。真正对
Call<Person> call = requestService.postStudent(new Student(),"xiMing");进行实现的是
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
ServiceMethod 实现的是对@POST("{user}/tt.php")Call<Person> postStudent(@Body Student student,@Path("user") String user)的解析。也可以说Retrofit最核心的功能是如何解析我们自定义的方法
重要的事情说三遍
Retrofit最核心的功能是如何解析我们自定义的方法
Retrofit最核心的功能是如何解析我们自定义的方法
Retrofit最核心的功能是如何解析我们自定义的方法
ServiceMethod 对自定义的方法的解析
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
return new ServiceMethod<>(this);
}
0. Factory 和Converter以及CallAdapter
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
无论是Converter.Factory还是CallAdapter.Factory都是提供一个规则,一个生产Converter或是CallAdapter的规则。
比如CallAdapter.Factory的规则是 public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,Retrofit retrofit);根据returnType的类型去创建不同的Adapter例如ExecutorCallAdapterFactory中就看是不是Call.class,用户也可以根据自己的需求去定义get,来实现CallAdapter的自定义。
Converter或是CallAdapter提供一个处理数据的规则。
类似我们的每条HTTP就是企业不同的工作任务 CallAdapter.Factory相当于HR,CallAdapter相当于员工。不同的http任务有不同type的技能,HR根据不同type找能处理这种type任务的员工。企业通过work指令让具体的CallAdapter(员工)根据自己的技能处理任务。adapterFactories 是表示有一群HR,有些HR能找到会计算机的,有些HR能找到厨师,所以在企业发布任务的时候回问每个HR,如果HR(小明)能找到就把CallAdapter生成,如果不能就返回null(告诉不能)企业在找下一个HR(小芳)
1.CallAdapter加载和构建
CallAdapter是通过CallAdapter.Factory创建的。
我们分析callAdapter = createCallAdapter();获得的是ExecutorCallAdapterFactory创建的CallAdapter
1、CallAdapter的作用
先说XXXCallAdapterFactory的作用,个人感觉提供一个拓展的方法。我们OKhttp提供的是ExecutorCallAdapterFactory(默认的)
Call<Person> postStudent(@Body Student student);
对于RxJava提供的是
Observable<Person> postStudent(@Body Student student);
同样我们通过addCallAdapterFactory(RxJavaCallAdapterFactory.create())
所以这个addCallAdapter将控制器(Call<XXX>或是Observable<XXX>)变的可以灵活定义了。同时,我们XXCallAdapterFactory提供了一个Get方法下面是ExecutorCallAdapterFactory的get方法的实现。ExecutorCallAdapterFactory只处理Call的,其他类型不处理。
也就是说我们可以同时提供多个CallAdapter每个都处理自己类型的returnType,这就是为什么我们adapterFactories 是用一个List进行保存。
2、ExecutorCallAdapterFactory是如何获得的。
private final List<CallAdapter.Factory> adapterFactories;
adapterFactories在Retrofit build的时候已经默认添加了。
对于converterFactories过程类似就不在说明了。
对Converter.Factory中的两个方法说明
1.requestBodyConverter 是在Post中@Body时被调用的。位置在parseParameter() parseParameter是被ServiceMethod.Builder.build()过程中调用的。
2.responseBodyConverter是在ServiceMethod.Builder.build()中。
//对ResponseBody的转换规则
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
//在post方法中我们要传RequestBody的转换规则
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
第二纬度看问题
每个部分注解都是哪些方法解析的
@POST("{user}/tt.php")
Call<Person> postStudent(@Body Student student, @Path("user") String user);
@POST(请求方法)的解析过程
关于字段的使用可以参考Retrofit网络请求参数注解
在构建ServiceMethod.build()的时候被解析出来。
注意:
1.这是{user}在这里只被保存起来,不会被替换
2.在http://xxx/api/News?newsId=1其中newsId=1也没有被拼接起来。
也就是说请求的method是已经确定了。但是完整的url并没有被组装起来
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
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;
}
}
完整URL的组装过程
这个过程比较复杂
主要是在ServiceMethod build()过程调用了parseParameterAnnotation
这里对ParameterHandler做一个解释:提供一个abstract void apply(RequestBuilder builder, T value),意思是apply的作用是将T中的数据按照需求构建我们的RequestBuilder ,就以@Url为例生成的是ParameterHandler实例是ParameterHandler.RelativeUrl 其中apply的实现是
@Override void apply(RequestBuilder builder, Object value) {
builder.setRelativeUrl(value);//将值直接设置为RelativeUrl。RelativeUrl就是相对路径,例如“api/getimage/xxxpng”
}
关于@Url的使用请参考Retrofit2-如何在请求时使用动态URL
关于其他类型(@Path等)的就不一一举例了。
ParameterHandler的另一个解释是保存了类型的解析方法。但是不进行解析。
这些类型包括(@Url,@Path,@Query,@QueryMap,@Header,@HeaderMap,@Field,@FieldMap,@Part)
解析是在OkHttpCall 中 createRawCall()过程中Request request = serviceMethod.toRequest(args);调用了 handlers[p].apply(requestBuilder, args[p]);
/** Builds an HTTP request from method arguments. */
Request toRequest(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 + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
同时经过这两步OKhttp需要发送请求设置参数的过程就全部完成了。接下来就是解析Response了
在说解析Response前,我个人的一个感觉。框架做了许多的容器。用来保存需要处理的东西。然后在不同的步骤进行处理。比如toRequest的步骤其实在ServiceMethod build()是可以完全处理完成的。作者这样写可以让处理过程推迟到必要的时候对如果要放弃任务就减少工作量。
总结
对于@POST("{user}/tt.php")Call<Person> postStudent(@Body Student student,@Path("user") String user)的解析大体是两个部分
1 ServiceMethod Builder.build()
2 OkHttpCall中createRawCall()调用serviceMethod.toRequest(args);