準(zhǔn)備知識(shí)
我眼中的Java-Type體系(2)對(duì)Method解析的時(shí)候要用到
在分析源碼前有哪些疑問呢?
CallAdapter是什么加勤?
Converter是什么?
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
保存Converter為什么是一個(gè)List?
private List<Converter.Factory> converterFactories = new ArrayList<>();
保存CallAdapter為什么也是一個(gè)List家制?
無論你是不是對(duì)上面有疑問秕重,在看完之后相信你都會(huì)有答案了不同。
總結(jié)
- Retrofit是OKHttp輔助工具
- Retrofit本身是一個(gè)注解解析器
- Retrofit可以和RxJava更容易配合使用
- ServiceMethod 的主要作用是解析構(gòu)建Request 和Response的解析
- OkHttpCall 主要作用是實(shí)現(xiàn)OKhttp3的調(diào)用
- CallAdapter 作用Retrofit.Call命令器的轉(zhuǎn)換成自定義的命令器
- Converter<?, RequestBody> requestBodyConverter是實(shí)現(xiàn)Request自定義的類轉(zhuǎn)換成RequestBody
- Converter<ResponseBody, ?> responseBodyConverter是將ResponseBody轉(zhuǎn)換成我們自定義的類
在對(duì)源碼進(jìn)行分析之前先看一下Retrofit,類是很少的溶耘。
我們分析Retrofit2二拐,帶著設(shè)計(jì)者實(shí)現(xiàn)功能的想法去分析。下面是對(duì)1到4點(diǎn)的說明
- 構(gòu)建OKHttpClient 幫我們實(shí)現(xiàn)Http請(qǐng)求
- 構(gòu)建Retrofit凳兵,保存相關(guān)的參數(shù)(參數(shù)作用后面會(huì)提及)
- 實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求
- 構(gòu)建Http請(qǐng)求的傳遞參數(shù)和返回參數(shù)
在分析Retrofit前我們看一下Okhttp請(qǐng)求
大概的步驟是
1創(chuàng)建一個(gè)OKHTTPClient(配置連接時(shí)間百新,讀寫時(shí)間,超時(shí)等參數(shù))
2創(chuàng)建一個(gè)Request(配置url留荔,head吟孙,get,post方法聚蝶,body等)
3發(fā)送請(qǐng)求獲得response
4 對(duì)response進(jìn)行解析
這也是一個(gè)HTTP請(qǐng)求的過程杰妓。
正式開始我們分析的旅程
Retrofit提供一些輔助類讓我們更加方便的
我們現(xiàn)在有一個(gè)像www.ss.com/xiMing/tt.php發(fā)送一個(gè)Student ,返回一個(gè)Person的需求。(假設(shè)我們用Json格式數(shù)據(jù)傳輸)如果我們用OKHTTP的方式碘勉,我們要把Student轉(zhuǎn)換成json格式巷挥,在服務(wù)器返回的時(shí)候又要把json格式轉(zhuǎn)換成Person。如果我們有多次這樣的請(qǐng)求验靡,我們每個(gè)都要這樣做倍宾。Retrofit把這個(gè)過程給我們處理了雏节。而之前Retrofit做的,我們不需要關(guān)注的高职,就是我們今天要學(xué)習(xí)的钩乍。
對(duì) @POST("{user}/tt.php")Call<Person> postStudent(@Body Student student,@Path("user") String user);解析最核心的類是ServiceMethod類。
create方法是創(chuàng)建RequestService的實(shí)現(xiàn)怔锌。創(chuàng)建一個(gè)interface的實(shí)現(xiàn)寥粹,要么用生成工具,要么用反射埃元。作者使用了后者涝涤。Proxy.newProxyInstance反射實(shí)現(xiàn)了RequestService。真正對(duì)
Call<Person> call = requestService.postStudent(new Student(),"xiMing");進(jìn)行實(shí)現(xiàn)的是
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
ServiceMethod 實(shí)現(xiàn)的是對(duì)@POST("{user}/tt.php")Call<Person> postStudent(@Body Student student,@Path("user") String user)的解析岛杀。也可以說Retrofit最核心的功能是如何解析我們自定義的方法
重要的事情說三遍
Retrofit最核心的功能是如何解析我們自定義的方法
Retrofit最核心的功能是如何解析我們自定義的方法
Retrofit最核心的功能是如何解析我們自定義的方法
ServiceMethod 對(duì)自定義的方法的解析
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都是提供一個(gè)規(guī)則阔拳,一個(gè)生產(chǎn)Converter或是CallAdapter的規(guī)則。
比如CallAdapter.Factory的規(guī)則是 public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,Retrofit retrofit);根據(jù)returnType的類型去創(chuàng)建不同的Adapter例如ExecutorCallAdapterFactory中就看是不是Call.class类嗤,用戶也可以根據(jù)自己的需求去定義get糊肠,來實(shí)現(xiàn)CallAdapter的自定義。
Converter或是CallAdapter提供一個(gè)處理數(shù)據(jù)的規(guī)則遗锣。
類似我們的每條HTTP就是企業(yè)不同的工作任務(wù) CallAdapter.Factory相當(dāng)于HR罪针,CallAdapter相當(dāng)于員工。不同的http任務(wù)有不同type的技能黄伊,HR根據(jù)不同type找能處理這種type任務(wù)的員工泪酱。企業(yè)通過work指令讓具體的CallAdapter(員工)根據(jù)自己的技能處理任務(wù)。adapterFactories 是表示有一群HR还最,有些HR能找到會(huì)計(jì)算機(jī)的墓阀,有些HR能找到廚師,所以在企業(yè)發(fā)布任務(wù)的時(shí)候回問每個(gè)HR拓轻,如果HR(小明)能找到就把CallAdapter生成斯撮,如果不能就返回null(告訴不能)企業(yè)在找下一個(gè)HR(小芳)
1.CallAdapter加載和構(gòu)建
CallAdapter是通過CallAdapter.Factory創(chuàng)建的。
我們分析callAdapter = createCallAdapter();獲得的是ExecutorCallAdapterFactory創(chuàng)建的CallAdapter
1扶叉、CallAdapter的作用
先說XXXCallAdapterFactory的作用勿锅,個(gè)人感覺提供一個(gè)拓展的方法。我們OKhttp提供的是ExecutorCallAdapterFactory(默認(rèn)的)
Call<Person> postStudent(@Body Student student);
對(duì)于RxJava提供的是
Observable<Person> postStudent(@Body Student student);
同樣我們通過addCallAdapterFactory(RxJavaCallAdapterFactory.create())
所以這個(gè)addCallAdapter將控制器(Call<XXX>或是Observable<XXX>)變的可以靈活定義了枣氧。同時(shí)溢十,我們XXCallAdapterFactory提供了一個(gè)Get方法下面是ExecutorCallAdapterFactory的get方法的實(shí)現(xiàn)。ExecutorCallAdapterFactory只處理Call的达吞,其他類型不處理张弛。
也就是說我們可以同時(shí)提供多個(gè)CallAdapter每個(gè)都處理自己類型的returnType,這就是為什么我們adapterFactories 是用一個(gè)List進(jìn)行保存。
2吞鸭、ExecutorCallAdapterFactory是如何獲得的寺董。
private final List<CallAdapter.Factory> adapterFactories;
adapterFactories在Retrofit build的時(shí)候已經(jīng)默認(rèn)添加了。
對(duì)于converterFactories過程類似就不在說明了刻剥。
對(duì)Converter.Factory中的兩個(gè)方法說明
1.requestBodyConverter 是在Post中@Body時(shí)被調(diào)用的遮咖。位置在parseParameter() parseParameter是被ServiceMethod.Builder.build()過程中調(diào)用的。
2.responseBodyConverter是在ServiceMethod.Builder.build()中造虏。
//對(duì)ResponseBody的轉(zhuǎn)換規(guī)則
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
//在post方法中我們要傳RequestBody的轉(zhuǎn)換規(guī)則
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
第二緯度看問題
每個(gè)部分注解都是哪些方法解析的
@POST("{user}/tt.php")
Call<Person> postStudent(@Body Student student, @Path("user") String user);
@POST(請(qǐng)求方法)的解析過程
關(guān)于字段的使用可以參考Retrofit網(wǎng)絡(luò)請(qǐng)求參數(shù)注解
在構(gòu)建ServiceMethod.build()的時(shí)候被解析出來盯滚。
注意:
1.這是{user}在這里只被保存起來,不會(huì)被替換
2.在http://xxx/api/News?newsId=1其中newsId=1也沒有被拼接起來酗电。
也就是說請(qǐng)求的method是已經(jīng)確定了。但是完整的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的組裝過程
這個(gè)過程比較復(fù)雜
主要是在ServiceMethod build()過程調(diào)用了parseParameterAnnotation
這里對(duì)ParameterHandler做一個(gè)解釋:提供一個(gè)abstract void apply(RequestBuilder builder, T value)内列,意思是apply的作用是將T中的數(shù)據(jù)按照需求構(gòu)建我們的RequestBuilder 撵术,就以@Url為例生成的是ParameterHandler實(shí)例是ParameterHandler.RelativeUrl 其中apply的實(shí)現(xiàn)是
@Override void apply(RequestBuilder builder, Object value) {
builder.setRelativeUrl(value);//將值直接設(shè)置為RelativeUrl。RelativeUrl就是相對(duì)路徑话瞧,例如“api/getimage/xxxpng”
}
關(guān)于@Url的使用請(qǐng)參考Retrofit2-如何在請(qǐng)求時(shí)使用動(dòng)態(tài)URL
關(guān)于其他類型(@Path等)的就不一一舉例了嫩与。
ParameterHandler的另一個(gè)解釋是保存了類型的解析方法。但是不進(jìn)行解析交排。
這些類型包括(@Url划滋,@Path,@Query埃篓,@QueryMap处坪,@Header,@HeaderMap架专,@Field同窘,@FieldMap,@Part)
解析是在OkHttpCall 中 createRawCall()過程中Request request = serviceMethod.toRequest(args);調(diào)用了 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();
}
同時(shí)經(jīng)過這兩步OKhttp需要發(fā)送請(qǐng)求設(shè)置參數(shù)的過程就全部完成了部脚。接下來就是解析Response了
在說解析Response前想邦,我個(gè)人的一個(gè)感覺∥酰框架做了許多的容器丧没。用來保存需要處理的東西。然后在不同的步驟進(jìn)行處理锡移。比如toRequest的步驟其實(shí)在ServiceMethod build()是可以完全處理完成的呕童。作者這樣寫可以讓處理過程推遲到必要的時(shí)候?qū)θ绻艞壢蝿?wù)就減少工作量。
總結(jié)
對(duì)于@POST("{user}/tt.php")Call<Person> postStudent(@Body Student student,@Path("user") String user)的解析大體是兩個(gè)部分
1 ServiceMethod Builder.build()
2 OkHttpCall中createRawCall()調(diào)用serviceMethod.toRequest(args);