Retrofit 是如何获取返回值 Call<T> 中的类型 T, 以提供给 Gson 使用的?

原创
2020/03/05 21:11
阅读数 6.5K

Call<T>的类型 T 即Response<T>的类型 T,获得了Call<T>就能够正确输出给Response<T>。那么Retrofit是怎么做到的呢?

以下是HttpServiceMethod<ResponseT, ReturnT>中的 static 方法:

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
										Retrofit retrofit, Method method, RequestFactory requestFactory) {
		// 省略部分代码
		// ...
		Type adapterType;
		// 解析 kotlin 语言中的返回值
		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 {
				// 解析 java 语言写的返回值(写过的都知道,这个很容易懂了)
		  		adapterType = method.getGenericReturnType();
		}
		// 省略部分代码
		// ...
}

拿到adapterType之后,又辗转调用了CallAdapter.Factory. get()以创建实现了CallAdapter接口的CallAdapter

public interface CallAdapter<R, T> {
		// 省略部分代码
		// ...
		abstract class Factory {
 				public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);
				// ...
		}
}

注意前面的adapterTypeCall<T>,所以要取出里面的T,才是真正要传给 Gson 从而输出的类型:

// 这是 CallAdapter.Factory. get() 的 impl
@Override public @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
		if (getRawType(returnType) != Call.class) {
				return null;
		}
		if (!(returnType instanceof ParameterizedType)) {
				throw new IllegalArgumentException("Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
		}
		final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
		// ...
		// 在这里 CallAdapter 接口的实现
		return new CallAdapter<Object, Call<?>>() {
				@Override public Type responseType() {
						return responseType;
				}
				// ...
		};
}

Call<T>有类似 GsonTypeToken的功能,如:

@KeepV
data class Data<T>(val users: List<T>)
@KeepV
data class User(val id: Long)

interface AaaService {
		@GET("a/b/c.json")
		fun getAbc(): Call<Data<User>>
}

能够正确解析出Data<User>,相当于new TypeToken<Data<User>>{}.getType(),所以能传给 Gson 从而 输入正确的Response<Data<User>>。细节就不多讲了,这里主要是做个笔记备忘。

题外话:
由于类型参数的 不可传递性,即:

<T> Type getType(T user) {
		return new TypeToken<T>{}.getType()
}
// 拿不到 Data 和 User 的类型
getType(new Data<User>())

Data<User>.class这种写法不能编译通过(kotlin里面可以),因此Data类内部不能拿到User类型,所以必须以某类的 运行时子类 + 泛型参数 的方式(Java 反射的实现如此,没有什么原理可讲)即new TypeToken<Data<User>>{}.getType()同时拿到DataUser(如果有更多泛型嵌套也可以拿到)的类型信息,所以才有了TypeToken

这从一个侧面说明了只要这种 字面量 Data<User>的写法能够编译通过(无论是用作返回值还是参数化类型),都有办法拿到实际类型。细心的人可能发现了上例中的val users: List<T>,这里值类型List<T>不是 字面量,但 Gson 也能拿到实际类型User,这也说明了只要在泛型类实例化时写了 字面量 的参数化类型,那么该类体中的一切类型占位符的实际类型也有办法拿到。

关于笔者运用这个规则而实现的一个框架见这里

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部