Retrofit interface 支持以下几种写法
/**
* 天气查询
* <https://wiki.viomi.net/pages/viewpage.action?pageId=211622019>
*/
interface IHttp {
@POST("api/app/weather/info/query")
suspend fun queryWeather(@Body request: QueryWeatherRequest): ResponseWrapper<QueryWeatherResponse>
@POST("api/app/weather/info/query")
fun queryWeatherCall(@Body request: QueryWeatherRequest): Call<ResponseWrapper<QueryWeatherResponse>>
@POST("api/app/weather/info/query")
fun queryWeatherSingle(@Body request: QueryWeatherRequest): Single<ResponseWrapper<QueryWeatherResponse>>
@POST("api/app/weather/info/query")
fun queryWeatherFuture(@Body request: QueryWeatherRequest): CompletableFuture<ResponseWrapper<QueryWeatherResponse>>
/**
* 错误写法,会抛出异常:
* java.lang.IllegalArgumentException: Unable to create call adapter for com.viomi.baseaction.network.ResponseWrapper<com.viomi.baseaction.weather.QueryDataResponse>
* for method IHttp.queryData
* Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for com.viomi.baseaction.network.ResponseWrapper<com.viomi.baseaction.weather.QueryDataResponse>.
* Tried:
* retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
* retrofit2.CompletableFutureCallAdapterFactory
* retrofit2.DefaultCallAdapterFactory
*/
@POST("api/app/weather/info/query")
fun queryWeatherError(@Body request: QueryWeatherRequest): ResponseWrapper<QueryWeatherResponse>
}
interface function 返回值类型是由 CallAdapter 提供的,Retrofit 内置两种类型:
class Platform {
List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
return hasJava8Types
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
}
如果添加了 RxJava Adapter api "com.squareup.retrofit2:adapter-rxjava2:${retrofitVersion}” 还可以支持 Observable、Single、Maybe、Completable 等类型
final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
@Override
public Object adapt(Call<R> call) {
Observable<Response<R>> responseObservable =
isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return RxJavaPlugins.onAssembly(observable);
}
}
对于 suspend function,Kotlin 编译器会在参数列表尾部添加一个 Continuation ,Retrofit 以此作为特征可以判断出当前方法是否为 suspend function,如果是则用 suspendCancellableCoroutine 进入协程的世界
suspend fun queryWeather(@Body request: QueryWeatherRequest): ResponseWrapper<QueryWeatherResponse>
// 反编译后的 java 代码
Object queryWeather(@Body QueryWeatherRequest queryWeatherRequest, Continuation<? super ResponseWrapper<QueryWeatherResponse>> continuation);
对 retrofit function 参数列表进行逐个处理,在此过程中可以判断出是否为 suspend function
Continuation 是添加到参数列表尾部的,所以只有最后一个参数才有可能是 Continuation ,其他位置的参数都为 falsefinal class RequestFactory {
static final class Builder {
private @Nullable ParameterHandler<?> parseParameter(
int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
ParameterHandler<?> result = null;
if (annotations != null) {
for (Annotation annotation : annotations) { // 处理标注在参数的 @Body、@Query 等注解
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;
}
}
if (result == null) { // 如果参数没有注解、且是参数列表里的最后一位、且是 Continuation 类型
if (allowContinuation) { // 那么说明此参数是由 Kotlin 编译器加上的,这个方法是 suspend function
try {
if (Utils.getRawType(parameterType) == Continuation.class) {
isKotlinSuspendFunction = true;
return null;
}
} catch (NoClassDefFoundError ignored) {
}
}
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
}
}
普通函数取返回值的类型(Call、Observable...)去查找 CallAdapter,而 suspend function 它的返回值是 Response or Body 类型,找不到对应的 CallAdapter,直接使用 DefaultCallAdapterFactory,也即对于 suspend funcation 来说它是用内置的 CallAdapter
然后通过 suspendCancellableCoroutine 挂起协程,在 retrofit2.Call#enqueue 回调里恢复协程,返回响应(Continuation.resume)或者抛出异常(Continuation.resumeWithException)
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
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
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
}
static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
try {
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
}
suspend fun <T : Any> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
val invocation = call.request().tag(Invocation::class.java)!!
val method = invocation.method()
val e = KotlinNullPointerException("Response from " +
method.declaringClass.name +
'.' +
method.name +
" was null but response body type was declared as non-null")
continuation.resumeWithException(e)
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}