第一眼看,跟我之前印象中的有點(diǎn)區(qū)別(也不知道是什么版本),return
的時候居然沒有adapt
方法了。開始還以為有什么重大的改變,其實(shí)也沒什么,只是將之前的adapt
方法封裝到invoke
方法中。
相關(guān)的method注解解析都放到ServiceMethod
中,有兩個關(guān)鍵函數(shù)調(diào)用,分別是RequestFactory
與HttpServiceMethod
的parseAnnotations()
方法。
static
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
RequestFactory
首先RequestFactory
中的parseAnnotations()
最終通過build()
方法來構(gòu)建一個RequestFactory
,用來保存解析出來的方法信息。
RequestFactory build() {
//1.解析方法上的注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler>[parameterCount];
//2.循環(huán)遍歷方法中的各個參數(shù),解析參數(shù)的注解
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
...
return new RequestFactory(this);
}
可以看到主要分為兩步:
- 通過
parseMethodAnnotation
來解析出請求的方式,例如GET
、POST
與PUT
等等;同時也會驗(yàn)證一些注解的合規(guī)使用,例如Multipart
與FormUrlEncoded
只能使用一個。 - 通過
parseParameter
來解析出請求的參數(shù)信息,例如Path
、Url
與Query
等等;同時也對它們的合規(guī)使用做了驗(yàn)證,例如QueryMap
與FieldMap
等注解它們的key
都必須為String
類型。這些注解的解析都是在parseParameterAnnotation()
方法中進(jìn)行的。
上面的p == lastParameter
需要特別注意下,為何要專門判斷該參數(shù)是否為最后一個呢?請繼續(xù)向下看。
協(xié)程的判斷條件
下面我們來著重看下parseParameter
的源碼,因?yàn)閺倪@里開始就涉及到協(xié)程的判斷。
private @Nullable ParameterHandler> parseParameter(
int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
ParameterHandler> result = null;
if (annotations != null) {
for (Annotation annotation : annotations) {
//1.解析方法參數(shù)的注解,并驗(yàn)證它們的合法性
ParameterHandler> annotationAction =
parseParameterAnnotation(p, parameterType, annotations, annotation);
if (annotationAction == null) {
continue;
}
//每個參數(shù)都只能有一個注解
if (result != null) {
throw parameterError(method, p,
"Multiple Retrofit annotations found, only one allowed.");
}
result = annotationAction;
}
}
//2.判斷是否是協(xié)程
if (result == null) {
if (allowContinuation) {
try {
if (Utils.getRawType(parameterType) == Continuation.class) {
isKotlinSuspendFunction = true;
return null;
}
} catch (NoClassDefFoundError ignored) {
}
}
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
第一點(diǎn)沒什么好說的,里面沒什么邏輯,就是一個純注解解析與Converter
的選取。
第二點(diǎn)是關(guān)鍵點(diǎn),用來判斷該方法的調(diào)用是否使用到了協(xié)程。同時有個allowContinuation
參數(shù),這個是什么呢?我們向上看,發(fā)現(xiàn)它是方法中的一個參數(shù),如果我們繼續(xù)追溯就會發(fā)現(xiàn)它就是我們之前特意需要注意的p == lastParameter
。
所以判斷是否是使用了協(xié)程有三步:
result
為空,即該參數(shù)沒有注解allowContinuation
為true
,即是最后一個參數(shù)Continuation.class
,說明該參數(shù)的類型為Continuation
只有符合上述三點(diǎn)才能證明使用了協(xié)程,但腦海里回想一下協(xié)程的寫法,發(fā)現(xiàn)完全對不到這三點(diǎn)...
到這里可能有的讀者已經(jīng)開始蒙圈了,如果你沒有深入了解協(xié)程的話,這個是正常的狀態(tài)。
別急,要理解這塊,還需要一點(diǎn)協(xié)程的原理知識,下面我來簡單說一下協(xié)程的部分實(shí)現(xiàn)原理。
suspend原理
我們先來看下使用協(xié)程是怎么寫的:
@GET("/v2/news")
suspend fun newsGet(@QueryMap params: Map
這是一個標(biāo)準(zhǔn)的協(xié)程寫法,然后我們再套用上面的條件,發(fā)現(xiàn)完全匹配不到。
因?yàn)椋@是不協(xié)程的本來面目。我們思考一個問題,為什么使用協(xié)程要添加suspend
關(guān)鍵字呢?這是重點(diǎn)。你可以多想幾分鐘。
(幾分鐘之后...)
不吊大家胃口了,我這里就直接說結(jié)論。
因?yàn)樵诖a編譯的過程中會自動為帶有suspend
的函數(shù)添加一個Continuation
類型的參數(shù),并將其添加到最后面。所以上面的協(xié)程真正的面目是這樣的:
@GET("/v2/news")
fun newsGet(@QueryMap params: Map
現(xiàn)在我們再來看上面的條件,發(fā)現(xiàn)能夠全部符合了。
由于篇幅有限,有關(guān)協(xié)程的原理實(shí)現(xiàn)就點(diǎn)到為止,后續(xù)我會專門寫一個協(xié)程系列,希望到時能夠讓讀者們認(rèn)識到協(xié)程的真面目,大家可以期待一下。
現(xiàn)在我們已經(jīng)知道了Retrofit
如何判斷一個方法是否使用了協(xié)程。那么我們再進(jìn)入另一個點(diǎn):
Retrofit
如何將Call
直接轉(zhuǎn)化為NewResonse
,簡單的說就是支持使newsGet
方法返回NewsResponse
。而這一步的轉(zhuǎn)化在HttpServiceMethod
中。
HttpServiceMethod
上面已經(jīng)分析完RequestFactory
的parseAnnotations()
,現(xiàn)在再來看下HttpServiceMethod
中的parseAnnotations()
。
static
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
// 1. 是協(xié)程
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType = Utils.getParameterLowerBound(0,
(ParameterizedType) parameterTypes[parameterTypes.length - 1]);
// 2. 判斷接口方法返回的類型是否是Response
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
// TODO figure out if type is nullable or not
// Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
// Find the entry for method
// Determine if return type is nullable or not
}
// 3. 注意:將方法返回類型偽裝成Call類型,并將SkipCallbackExecutor注解添加到annotations中
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
// 4. 創(chuàng)建CallAdapter,適配call,將其轉(zhuǎn)化成需要的類型
CallAdapter
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
// 5. 創(chuàng)建Converter,將響應(yīng)的數(shù)據(jù)轉(zhuǎn)化成對應(yīng)的model類型
Converter
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
// 6. 接口方法不是協(xié)程
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
// 7. 接口方法是協(xié)程,同時返回類型是Response類型
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod
callFactory, responseConverter, (CallAdapter
} else {
// 8. 接口方法是協(xié)程,同時返回類型是body,即自定義的model類型
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod
callFactory, responseConverter, (CallAdapter
continuationBodyNullable);
}
}
代碼中已經(jīng)解析的很清楚了,需要注意3,如果是協(xié)程會做兩步操作,首先將接口方法的返回類型偽裝成Call
類型,然后再將SkipCallbackExecutor
手動添加到annotations
中。字面意思就在后續(xù)調(diào)用callAdapter.adapt(call)
時,跳過創(chuàng)建Executor
,簡單理解就是協(xié)程不需要Executor
來切換線程的。為什么這樣?這一點(diǎn)先放這里,后續(xù)創(chuàng)建Call
的時候再說。
我們直接看協(xié)程的7,8部分。7也不詳細(xì)分析,簡單提一下,它就是返回一個Response
的類型,這個Retrofit
最基本的支持了。至于如何在使用協(xié)程時將Call
轉(zhuǎn)化成Response
原理與8基本相同,只是比8少一步,將它的body
轉(zhuǎn)化成對應(yīng)的返回類型model
。所以下面我們直接看8。
將Call轉(zhuǎn)化成對應(yīng)的Model
static final class SuspendForBody
private final CallAdapter
private final boolean isNullable;
SuspendForBody(RequestFactory requestFactory, okhttp3.Call.Fac
《Android學(xué)習(xí)筆記總結(jié)+最新移動架構(gòu)視頻+大廠安卓面試真題+項(xiàng)目實(shí)戰(zhàn)源碼講義》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整資料開源分享
tory callFactory,
Converter
CallAdapter
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
this.isNullable = isNullable;
}
@Override protected Object adapt(Call
// 1. 獲取適配的Call
call = callAdapter.adapt(call);
//noinspection unchecked Checked by reflection inside RequestFactory.
// 2. 獲取協(xié)程的Continuation
Continuation
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
}
}
我們的關(guān)注點(diǎn)在adapt
,文章開頭已經(jīng)說了,新版的Retrofit
將adapt
隱藏到invoke
中。而invoke
中調(diào)用的就是這個adapt
。
首先第一步,適配Call
,如果是RxJava
,這里的callAdapter
就是RxJava2CallAdapter
,同時返回的就是Observable
,這個之前看過源碼的都知道。
但現(xiàn)在是協(xié)程,那么這個時候的callAdapter
就是Retrofit
默認(rèn)的DefaultCallAdapterFactory
@Override public @Nullable CallAdapter, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
// 1. 注意: 如果是協(xié)程,因?yàn)榻涌诜椒ǚ祷貨]有使用Call,之前3的第一步偽裝成Call的處理就在這里體現(xiàn)了作用
if (getRawType(returnType) != Call.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call
}
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
// 2. 之前3的第二部就在這里體現(xiàn),由于之前已經(jīng)將SkipCallbackExecutor注解添加到annotations中,所以Executor直接為null
final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter
寫在最后
本次我的分享也接近尾聲了,感謝你們在百忙中花上一下午來這里聆聽我的宣講,希望在接下來的日子,我們共同成長,一起進(jìn)步?。?!
最后放上一個大概的Android學(xué)習(xí)方向及思路(詳細(xì)的內(nèi)容太多了~),提供給大家:
對于程序員來說,要學(xué)習(xí)的知識內(nèi)容、技術(shù)有太多太多,這里就先放上一部分,其他的內(nèi)容有機(jī)會在后面的文章向大家呈現(xiàn)出來,不過我自己所有的學(xué)習(xí)資料都整理成了一個文檔,一直在不斷學(xué)習(xí),希望能幫助到大家,也節(jié)省大家在網(wǎng)上搜索資料的時間來學(xué)習(xí),也可以分享動態(tài)給身邊好友一起學(xué)習(xí)!
為什么某些人會一直比你優(yōu)秀,是因?yàn)樗旧砭秃軆?yōu)秀還一直在持續(xù)努力變得更優(yōu)秀,而你是不是還在滿足于現(xiàn)狀內(nèi)心在竊喜!希望讀到這的您能點(diǎn)個小贊和關(guān)注下我,以后還會更新技術(shù)干貨,謝謝您的支持!
Android架構(gòu)師之路很漫長,一起共勉吧!
如果你覺得文章寫得不錯就給個贊唄?如果你覺得那里值得改進(jìn)的,請給我留言,一定會認(rèn)真查詢,修正不足,謝謝。
本文已被CODING開源項(xiàng)目:《Android學(xué)習(xí)筆記總結(jié)+移動架構(gòu)視頻+大廠面試真題+項(xiàng)目實(shí)戰(zhàn)源碼》收錄