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

相關的method注解解析都放到ServiceMethod中,有兩個關鍵函數調用,分別是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()方法來構建一個RequestFactory,用來保存解析出來的方法信息。

RequestFactory build() {
//1.解析方法上的注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler[parameterCount];
//2.循環遍歷方法中的各個參數,解析參數的注解
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等等;同時也會驗證一些注解的合規使用,例如MultipartFormUrlEncoded只能使用一個。
  2. 通過parseParameter來解析出請求的參數信息,例如PathUrlQuery等等;同時也對它們的合規使用做了驗證,例如QueryMapFieldMap等注解它們的key都必須為String類型。這些注解的解析都是在parseParameterAnnotation()方法中進行的。

上面的p == lastParameter需要特別注意下,為何要專門判斷該參數是否為最后一個呢?請繼續向下看。

協程的判斷條件

下面我們來著重看下parseParameter的源碼,因為從這里開始就涉及到協程的判斷。

private @Nullable ParameterHandler parseParameter(
int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
ParameterHandler result = null;
if (annotations != null) {
for (Annotation annotation : annotations) {
//1.解析方法參數的注解,并驗證它們的合法性
ParameterHandler annotationAction =
parseParameterAnnotation(p, parameterType, annotations, annotation);

if (annotationAction == null) {
continue;
}

//每個參數都只能有一個注解
if (result != null) {
throw parameterError(method, p,
"Multiple Retrofit annotations found, only one allowed.");
}

result = annotationAction;
}
}

//2.判斷是否是協程
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;
}

第一點沒什么好說的,里面沒什么邏輯,就是一個純注解解析與Converter的選取。

第二點是關鍵點,用來判斷該方法的調用是否使用到了協程。同時有個allowContinuation參數,這個是什么呢?我們向上看,發現它是方法中的一個參數,如果我們繼續追溯就會發現它就是我們之前特意需要注意的p == lastParameter

所以判斷是否是使用了協程有三步:

  1. result為空,即該參數沒有注解
  2. allowContinuationtrue,即是最后一個參數
  3. Continuation.class,說明該參數的類型為Continuation

只有符合上述三點才能證明使用了協程,但腦海里回想一下協程的寫法,發現完全對不到這三點...

到這里可能有的讀者已經開始蒙圈了,如果你沒有深入了解協程的話,這個是正常的狀態。

別急,要理解這塊,還需要一點協程的原理知識,下面我來簡單說一下協程的部分實現原理。

suspend原理

我們先來看下使用協程是怎么寫的:

@GET("/v2/news")
suspend fun newsGet(@QueryMap params: Map): NewsResponse

這是一個標準的協程寫法,然后我們再套用上面的條件,發現完全匹配不到。

因為,這是不協程的本來面目。我們思考一個問題,為什么使用協程要添加suspend關鍵字呢?這是重點。你可以多想幾分鐘。

(幾分鐘之后...)

不吊大家胃口了,我這里就直接說結論。

因為在代碼編譯的過程中會自動為帶有suspend的函數添加一個Continuation類型的參數,并將其添加到最后面。所以上面的協程真正的面目是這樣的:

@GET("/v2/news")
fun newsGet(@QueryMap params: Map, c: Continuation): NewsResponse

現在我們再來看上面的條件,發現能夠全部符合了。

由于篇幅有限,有關協程的原理實現就點到為止,后續我會專門寫一個協程系列,希望到時能夠讓讀者們認識到協程的真面目,大家可以期待一下。

現在我們已經知道了Retrofit如何判斷一個方法是否使用了協程。那么我們再進入另一個點:

Retrofit如何將Call直接轉化為NewResonse,簡單的說就是支持使newsGet方法返回NewsResponse。而這一步的轉化在HttpServiceMethod中。

HttpServiceMethod

上面已經分析完RequestFactoryparseAnnotations(),現在再來看下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. 是協程
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. 創建CallAdapter,適配call,將其轉化成需要的類型
CallAdapter callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
// 5. 創建Converter,將響應的數據轉化成對應的model類型
Converter responseConverter =
createResponseConverter(retrofit, method, responseType);

okhttp3.Call.Factory callFactory = retrofit.callFactory;
// 6. 接口方法不是協程
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
// 7. 接口方法是協程,同時返回類型是Response類型
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod) new SuspendForResponse<>(requestFactory,
callFactory, responseConverter, (CallAdapter>) callAdapter);
} else {
// 8. 接口方法是協程,同時返回類型是body,即自定義的model類型
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod) new SuspendForBody<>(requestFactory,
callFactory, responseConverter, (CallAdapter>) callAdapter,
continuationBodyNullable);
}
}

代碼中已經解析的很清楚了,需要注意3,如果是協程會做兩步操作,首先將接口方法的返回類型偽裝成Call類型,然后再將SkipCallbackExecutor手動添加到annotations中。字面意思就在后續調用callAdapter.adapt(call)時,跳過創建Executor,簡單理解就是協程不需要Executor來切換線程的。為什么這樣?這一點先放這里,后續創建Call的時候再說。

我們直接看協程的7,8部分。7也不詳細分析,簡單提一下,它就是返回一個Response的類型,這個Retrofit最基本的支持了。至于如何在使用協程時將Call轉化成Response原理與8基本相同,只是比8少一步,將它的body轉化成對應的返回類型model。所以下面我們直接看8。

將Call轉化成對應的Model

static final class SuspendForBody extends HttpServiceMethod {
private final CallAdapter> callAdapter;
private final boolean isNullable;

SuspendForBody(RequestFactory requestFactory, okhttp3.Call.Fac

《Android學習筆記總結+最新移動架構視頻+大廠安卓面試真題+項目實戰源碼講義》

【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. 獲取協程的Continuation
Continuation continuation = (Continuation) args[args.length - 1];
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
}
}

我們的關注點在adapt,文章開頭已經說了,新版的Retrofitadapt隱藏到invoke中。而invoke中調用的就是這個adapt

首先第一步,適配Call,如果是RxJava,這里的callAdapter就是RxJava2CallAdapter,同時返回的就是Observable,這個之前看過源碼的都知道。

但現在是協程,那么這個時候的callAdapter就是Retrofit默認的DefaultCallAdapterFactory

@Override public @Nullable CallAdapter get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
// 1. 注意: 如果是協程,因為接口方法返回沒有使用Call,之前3的第一步偽裝成Call的處理就在這里體現了作用
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的第二部就在這里體現,由于之前已經將SkipCallbackExecutor注解添加到annotations中,所以Executor直接為null
final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;

return new CallAdapter>() {

寫在最后

本次我的分享也接近尾聲了,感謝你們在百忙中花上一下午來這里聆聽我的宣講,希望在接下來的日子,我們共同成長,一起進步!!!

最后放上一個大概的Android學習方向及思路(詳細的內容太多了~),提供給大家:

對于程序員來說,要學習的知識內容、技術有太多太多,這里就先放上一部分,其他的內容有機會在后面的文章向大家呈現出來,不過我自己所有的學習資料都整理成了一個文檔,一直在不斷學習,希望能幫助到大家,也節省大家在網上搜索資料的時間來學習,也可以分享動態給身邊好友一起學習!

為什么某些人會一直比你優秀,是因為他本身就很優秀還一直在持續努力變得更優秀,而你是不是還在滿足于現狀內心在竊喜!希望讀到這的您能點個小贊和關注下我,以后還會更新技術干貨,謝謝您的支持!

Android架構師之路很漫長,一起共勉吧!

如果你覺得文章寫得不錯就給個贊唄?如果你覺得那里值得改進的,請給我留言,一定會認真查詢,修正不足,謝謝。

本文已被CODING開源項目:《Android學習筆記總結+移動架構視頻+大廠面試真題+項目實戰源碼》收錄