第一眼看,跟我之前印象中的有點(diǎn)區(qū)別(也不知道是什么版本),return的時候居然沒有adapt方法了。開始還以為有什么重大的改變,其實(shí)也沒什么,只是將之前的adapt方法封裝到invoke方法中。

相關(guān)的method注解解析都放到ServiceMethod中,有兩個關(guān)鍵函數(shù)調(diào)用,分別是RequestFactoryHttpServiceMethodparseAnnotations()方法。

static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
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);
}

可以看到主要分為兩步:

  1. 通過parseMethodAnnotation來解析出請求的方式,例如GETPOSTPUT等等;同時也會驗(yàn)證一些注解的合規(guī)使用,例如MultipartFormUrlEncoded只能使用一個。
  2. 通過parseParameter來解析出請求的參數(shù)信息,例如Path、UrlQuery等等;同時也對它們的合規(guī)使用做了驗(yàn)證,例如QueryMapFieldMap等注解它們的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é)程有三步:

  1. result為空,即該參數(shù)沒有注解
  2. allowContinuationtrue,即是最后一個參數(shù)
  3. 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): NewsResponse

這是一個標(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, c: Continuation): NewsResponse

現(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)分析完RequestFactoryparseAnnotations(),現(xiàn)在再來看下HttpServiceMethod中的parseAnnotations()。

static HttpServiceMethod parseAnnotations(
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 callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
// 5. 創(chuàng)建Converter,將響應(yīng)的數(shù)據(jù)轉(zhuǎn)化成對應(yīng)的model類型
Converter responseConverter =
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) new SuspendForResponse<>(requestFactory,
callFactory, responseConverter, (CallAdapter>) callAdapter);
} else {
// 8. 接口方法是協(xié)程,同時返回類型是body,即自定義的model類型
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod) new SuspendForBody<>(requestFactory,
callFactory, responseConverter, (CallAdapter>) 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 extends HttpServiceMethod {
private final CallAdapter> 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 responseConverter,
CallAdapter> callAdapter, boolean isNullable) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
this.isNullable = isNullable;
}

@Override protected Object adapt(Call call, Object[] args) {
// 1. 獲取適配的Call
call = callAdapter.adapt(call);

//noinspection unchecked Checked by reflection inside RequestFactory.
// 2. 獲取協(xié)程的Continuation
Continuation continuation = (Continuation) args[args.length - 1];
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
}
}

我們的關(guān)注點(diǎn)在adapt,文章開頭已經(jīng)說了,新版的Retrofitadapt隱藏到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 or 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)源碼》收錄