場景
Retrofit + Okhttp3 是現(xiàn)在開發(fā)標(biāo)配的網(wǎng)絡(luò)請求框架
Okhttp3 負(fù)責(zé)底層請求邏輯
Retrofit 負(fù)責(zé)上層請求外觀桂对,以接口方法定義的形式去直觀表現(xiàn)
分析帶問題
1.如何將接口外觀(包括參數(shù),注解)轉(zhuǎn)換為Ok3的請求
2.如何將Ok3的響應(yīng)倦卖,解析為接口定義的返回昔馋,以及目標(biāo)業(yè)務(wù)數(shù)據(jù)類型
3.核心類,CallAdapter糖耸, Converter的職責(zé)和意義
場景設(shè)定
下面秘遏,是一個retrofit的請求定義:
@POST("/uoms/servlet/dispatch")
Observable<BaseResponse> dispatch(@Field String workNo);
@POST("/uoms/servlet/dispatchBatch")
Call<BaseResponse> dispatchBatch(@Body ApiRequest request);
可以看到,將繁雜嘉竟、多變的請求邦危,變換為了注解+方法參數(shù),返回參數(shù)的形式舍扰,非常直觀倦蚪,明了,根據(jù)上面的定義边苹,我們探尋一下調(diào)用過程和設(shè)計(jì)邏輯陵且。
解析接口
a) 我們先關(guān)注,最核心的類,所有業(yè)務(wù)的起點(diǎn) Retrofit:
public final class Retrofit {
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();//這是一個方法的完整解析后的對象
final okhttp3.Call.Factory callFactory;//OkhttpClient的引用
final HttpUrl baseUrl;//請求的基礎(chǔ)
final List<Converter.Factory> converterFactories;
final List<CallAdapter.Factory> callAdapterFactories;
...
}
劃重點(diǎn)慕购,這3個類代表了整個解析的核心:
ServiceMethod:
單個接口方法的邏輯抽象聊疲,為了加快調(diào)用速度,Retrofit對它做了緩存
Converter 和 Converter.Factory:
用于數(shù)據(jù)類型解析轉(zhuǎn)換沪悲,包括將參數(shù)解析為RequestBody以及將ResponseBody的數(shù)據(jù)解析為我們需要的類型
CallAdapter 和 CallAdapter.Factory:
用于生成返回外觀获洲,默認(rèn)是Retrofit的Call<?>,如果用了RxJava2Adapter庫殿如,返回形式可以變換到RxJava2的Obserable<?>形式
我們每次調(diào)用的時候贡珊,都是調(diào)用如下方法:
public <T> T create(final Class<T> service) {
...
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
...
@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.
...
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
}
});
}
代碼邏輯很簡單:
1.Retrofit巧用動態(tài)代理模式,攔截方法調(diào)用涉馁,獲取到Method簽名门岔,參數(shù)Argus,然后從Cache里面查找Method代表的 ServiceMethod烤送,如果沒有寒随,通過Method 生成一個ServiceMethod,并加入到Cache胯努,方便下次調(diào)用
2.整個調(diào)用邏輯牢裳,都封成了OkHttpCall,OkHttpCall通過ServiceMethod叶沛,Argus蒲讯,完成請求
3.最后調(diào)用serviceMethod.adapt(),返回灰署,這里實(shí)際調(diào)用的就是CallAdapter.Factory去轉(zhuǎn)換為我們想要的Call<?>或者Obserable<?>判帮,后面分析
b)ServiceMethod
ServiceMethod是所有調(diào)用的核心,包括怎么解析參數(shù)注解溉箕,怎么解析方法注解晦墙,包括返回形式是怎么返回,需要重點(diǎn)分析:
ServiceMethod<?, ?> loadServiceMethod(Method method) {
...
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
可以看到ServiceMethod的構(gòu)建很簡單肴茄,傳入Method晌畅,然后調(diào)用ServiceMethod的Build構(gòu)造函數(shù):
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
在構(gòu)造函數(shù)中,ServiceMethod已經(jīng)拿到了方法的注解詳細(xì)信息寡痰,方法的參數(shù)注解信息抗楔,方法的參數(shù)類型信息,后面都是圍繞這些數(shù)據(jù)展開處理拦坠。
然后我們再看ServiceMethod.Bulder.build()方法:
public ServiceMethod build() {
callAdapter = createCallAdapter();//查找CallAdapter
responseType = callAdapter.responseType();//獲取返回參數(shù)類型连躏,這里實(shí)際是Call<?>或者Observable<?>里面的泛型類型
...
responseConverter = createResponseConverter();// 通過ResponseType去Retrofit反查可以解析當(dāng)前ResponseType的Converter
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);//解析方法注解參數(shù),例如POST贞滨,HEAD之類
}
...省略各種請求合法性判定代碼...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
...省略參數(shù)類型合法性判定代碼...
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
...
//獲取到對應(yīng)參數(shù)類型的解析器入热,并保存到parameterHandlers數(shù)組里面,稍后解析真實(shí)請求的時候用到
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
...省略各種請求合法性判定代碼...
return new ServiceMethod<>(this);
}
所以我們可以到,上面的解析流程:
解析返回參數(shù)勺良,查找到對應(yīng)的CallAdapter绰播,保存 ->
解析方法注解,并保存相關(guān)請求信息 ->
解析方法參數(shù)注解郑气,查找對應(yīng)的ParameterHandler并保存 ->
完成返回 ServiceMethod實(shí)例
所以我們可以知道幅垮,ServiceMethod對象的構(gòu)建完成腰池,已經(jīng)確定了方法參數(shù)調(diào)用怎么解析尾组,請求返回怎么解析,剩下就是執(zhí)行真實(shí)的請求了
c) ServiceMethod.callAdapter的作用和來源
CallAdapter接口源碼如下:
public interface CallAdapter<R, T> {
//返回實(shí)際的返回參數(shù)類型示弓,Call<Repo> 這里的Type就是Repo.class
Type responseType();
//適配功能讳侨,返回Call<R>結(jié)果的包裝器
T adapt(Call<R> call);
//抽象工程定義
abstract class Factory {
//能否處理,通過這個方法來判定奏属,有效處理會返回CallAdapter實(shí)例跨跨,否則為NULL
public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
Retrofit retrofit);
...
}
}
CallAdapter,主要2個方法囱皿,一個提供返回值的確切類型勇婴,一個是將Call<R>轉(zhuǎn)換為其他形式的包裝器,比如返回 Observable<R>
CallAdapter.Factory嘱腥,抽象工程耕渴,get返回Null代表該Factory處理不了該returnType
我們可以看到之前的Retrofit.create():
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
這實(shí)際調(diào)用就是
return serviceMethod.callAdapter.adapt(okHttpCall);
始終處理對象,都是OkHttpCall類型
CallAdapter負(fù)責(zé)轉(zhuǎn)換返回值形式齿兔,基于Call<?>橱脸,轉(zhuǎn)換為我們想要的形式,我們看看這個serviceMethod.callAdapter怎么來的
查看ServiceMethod的createCallAdapter():
private CallAdapter<T, R> createCallAdapter() {
Type returnType = method.getGenericReturnType();//拿到方法的返回類型
...
Annotation[] annotations = method.getAnnotations();
try {
return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) {
...
}
}
我們再看看Retrofit的callAdapter():
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
...
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType,annotations, this);
if (adapter != null) {
return adapter;
}
}
...注意:如果上面代碼沒有查找到合適的CallAdapter分苇,一定拋出異常...
}
可以知道添诉,Retrofit通過ServiceMethod提供的returnType, annotations,循環(huán)遍歷callAdapterFactories医寿,來獲取可以處理該returnType的CallAdapter實(shí)例栏赴,在Retrofit.build()時候,默認(rèn)加入了CallAdapter.Factory的默認(rèn)實(shí)現(xiàn)DefaultCallAdapterFactory :
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
...
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
...
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return call;
}
};
}
}
所以默認(rèn)靖秩,我們就可以定義返回接口為 Call<?>類型
到此须眷,CallAdapter的作用和來源分析完畢
d) Converter 和 Converter.Factory 作用意義
Converter,源碼定義:
public interface Converter<F, T> {//將 F類型的數(shù)據(jù)盆偿,轉(zhuǎn)換為 T類型
T convert(F value) throws IOException;
...
}
Converter.Factory源碼定義:
public interface Converter<F, T> {
...
abstract class Factory {
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations, Retrofit retrofit) {
return null;
}
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
...
}
}
與CallAdapter.Factory類似柒爸,Converter.Factory可以處理某類型的數(shù)據(jù),則返回有效的Converter實(shí)例事扭,否則就返回null
Converter.Factory.requestBodyConverter();負(fù)責(zé)將對應(yīng)類型參數(shù)數(shù)據(jù)捎稚,轉(zhuǎn)換為RequestBody,這個方法在拼裝請求的時候,大量用到
Converter.Factory.responseBodyConverter();負(fù)責(zé)將ResponseBody的數(shù)據(jù)今野,轉(zhuǎn)換為對應(yīng)需要的類型
stringConverter();負(fù)責(zé)將對應(yīng)類型數(shù)據(jù)葡公,轉(zhuǎn)換為String形式
ConverterFactory負(fù)責(zé)生產(chǎn)3類Converter,一個是將參數(shù)轉(zhuǎn)變?yōu)镽equestBody形式的条霜,請求裝配Converter催什,一個是將ResponseBody解析為方法定義的返回值(這個是指的Call<T>里面的T類型),一個是將方法參數(shù)形式轉(zhuǎn)換為字符串
e) Converter使用:ParameterHandler
上面介紹了Converter作用宰睡,我們先看看最多使用的地方蒲凶。
ParameterHandler,對參數(shù)注解進(jìn)行解析拆内,先看定義:
abstract class ParameterHandler<T> {
abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;
...解析參數(shù)旋圆,加入進(jìn)ReuqestBuilder
}
注:RequestBuilder是Retrofit對構(gòu)建Ok3的Request邏輯封裝
這里可以看出,ParameterHandler負(fù)責(zé)將參數(shù)合理的裝配進(jìn)入RequestBuilder
它的實(shí)現(xiàn)類麸恍,我們應(yīng)該很眼熟:
ParameterHandler.Query
ParameterHandler.Header
ParameterHandler.Path
...
這些就是具體這些注解內(nèi)容灵巧,將以什么形式去apply到RequestBuilder里面,以
ParameterHandler.Query舉例:
static final class Query<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Query(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = checkNotNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) return; // Skip null values.
String queryValue = valueConverter.convert(value);
if (queryValue == null) return; // Skip converted but null values
builder.addQueryParam(name, queryValue, encoded);
}
}
很簡單抹沪,就是用Converter去轉(zhuǎn)換T類型的參數(shù)為字符串刻肄,然后拼接進(jìn)入RequestBuilder,我們再回到ServiceMethod.Build.build()方法融欧,解析方法注解:
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);
}
再看ServiceMethod.parseParameter()敏弃,最終調(diào)用的ServiceMethod.parseParameterAnnotation()
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
if (annotation instanceof Url) {
...
} else if (annotation instanceof Path) {
...
} else if (annotation instanceof Query) {
Query query = (Query) annotation;
String name = query.value();
boolean encoded = query.encoded();
Class<?> rawParameterType = Utils.getRawType(type);
gotQuery = true;
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{
...
}
...
}
流程:
判定注解類型 -> 是Query -> 去Retrofit查找合適的Converter -> 拿到Converter,生成 ParameterHandler.Query實(shí)例蹬癌,保存進(jìn)入ServiceMethod的數(shù)組里面权她,等待解析真實(shí)請求的時候調(diào)用。
我們再去跟蹤Retrofit.stringConverter()方法:
public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
...
for (int i = 0, count = converterFactories.size(); i < count; i++) {
Converter<?, String> converter = converterFactories.get(i).stringConverter(type, annotations, this);
if (converter != null) {
return (Converter<T, String>) converter;
}
}
...
return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
}
最終返回的是默認(rèn)實(shí)現(xiàn)逝薪,ToStringConverter.INSTANCE隅要,它也是簡單調(diào)用toString()方法而已。
至此董济,方法的參數(shù)解析也完畢了步清,
我們得到了一個ServiceMethod實(shí)例,它包含了:
1.請求地址、類型虏肾、頭部信息
2.包含了正確解析參數(shù)的ParameterHandler數(shù)組
3.包含了正確解析響應(yīng)類型的responseConverter
4.包含了方法的返回適配器callAdapter
發(fā)送請求
a)了解Retrofit.Call
Retrofit抽象出了自己的Call邏輯廓啊,一個可以操作的請求對象:
//請求過程抽象
public interface Call<T> extends Cloneable {
//返回有效的Response
Response<T> execute() throws IOException;
//異步請求
void enqueue(Callback<T> callback);
boolean isExecuted();
//主動取消請求
void cancel();
/** True if {@link #cancel()} was called. */
boolean isCanceled();
Call<T> clone();
//轉(zhuǎn)換出原始的Ok3請求
Request request();
}
//異步回調(diào)
public interface Callback<T> {
void onResponse(Call<T> call, Response<T> response);
void onFailure(Call<T> call, Throwable t);
}
//Retrofit返回結(jié)果封裝
public final class Response<T> {
...
private final okhttp3.Response rawResponse;
private final @Nullable T body;
private final @Nullable ResponseBody errorBody;
...
/** The raw response from the HTTP client. */
public okhttp3.Response raw() {
return rawResponse;
}
/** HTTP status code. */
public int code() {
return rawResponse.code();
}
/** HTTP status message or null if unknown. */
public String message() {
return rawResponse.message();
}
/** HTTP headers. */
public Headers headers() {
return rawResponse.headers();
}
/** Returns true if {@link #code()} is in the range [200..300). */
public boolean isSuccessful() {
return rawResponse.isSuccessful();
}
/** The deserialized response body of a {@linkplain #isSuccessful() successful} response. */
public @Nullable T body() {
return body;
}
...
}
Retrofit,封裝了完整的Call, Callback, Response封豪,對底層Ok3做進(jìn)一步透明化
我們再看Call具體實(shí)現(xiàn)OkhttpCall具體執(zhí)行調(diào)用:
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
...
call = rawCall = createRawCall();
...
return parseResponse(call.execute());
}
@Override
public void enqueue(final Callback<T> callback) {
...
okhttp3.Call call;
synchronized (this) {
...
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
...
}
}
}
...
call.enqueue(new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
...
response = parseResponse(rawResponse);
...
callback.onResponse(OkHttpCall.this, response);
...
}
...
});
}
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = serviceMethod.toCall(args);//這里負(fù)責(zé)轉(zhuǎn)換
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
...
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);//這里負(fù)責(zé)轉(zhuǎn)換
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
...
}
}
這里我只列出了核心代碼谴轮,可以看出,最終都是調(diào)用都是ServiceMethod.toCall()方法生成請求吹埠,跟蹤toCall()源碼:
okhttp3.Call toCall(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
...
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
...
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return callFactory.newCall(requestBuilder.build());
}
終于用到我們上面分析保存的信息了第步,ServiceMethod的相關(guān)屬性疮装,ParameterHandler數(shù)組,生成RequestBuilder粘都,再構(gòu)建出Ok3的Request廓推,生成Ok3的Call。
注: 這里callFactory指的是OkHttpClient
然后收到響應(yīng)的適合翩隧,都調(diào)用ServiceMethod.toResponse():
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
serviceMethod.responseConverter在這里解析響應(yīng)結(jié)果類型
到此樊展,整個請求調(diào)用過程,跟蹤完畢
總結(jié)流程
CallAdapter堆生,轉(zhuǎn)換響應(yīng)形式专缠,默認(rèn)是Call<T>,有興趣的再去看看RxJava2Adapter下面的幾個類
Converter顽频,轉(zhuǎn)換參數(shù)類型藤肢,Converter.Factory轉(zhuǎn)換參數(shù)到RequestBody太闺,ResponseBody到參數(shù)
1.生成ServiceMethod糯景,解析了方法注解,保存了核心的請求屬性省骂,頭部蟀淮,其次是ParameterHandler數(shù)組用于解析方法參數(shù)值、類型钞澳,方法參數(shù)的注解
2.生成OkhttpCall怠惶,內(nèi)部調(diào)用ServiceMethod拼裝請求 和 解析響應(yīng)
3.請求的生成靠 ServiceMethod.toRequest() 響應(yīng)解析靠ServiceMethod.toResponse(),實(shí)際都是依賴第一步的解析保存的信息
其他
我們可以自己基于現(xiàn)有的RxJava2Adapter, GsonConvertFactory轧粟,重寫CallAdapter策治,Converter,以達(dá)到我們的目標(biāo)兰吟,比如:統(tǒng)一處理請求錯誤通惫,統(tǒng)一處理額外業(yè)務(wù)數(shù)據(jù)(resultCode, resultMessage之類的)