图文+视频双管齐下,带你全面彻底理解Retrofit源码,学完还不懂请砍我!【墙裂建议收藏】

77 阅读7分钟

下面我们来着重看下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<String, String>): NewsResponse

这是一个标准的协程写法,然后我们再套用上面的条件,发现完全匹配不到。

因为,这是不协程的本来面目。我们思考一个问题,为什么使用协程要添加suspend关键字呢?这是重点。你可以多想几分钟。

(几分钟之后...)

不吊大家胃口了,我这里就直接说结论。

因为在代码编译的过程中会自动为带有suspend的函数添加一个Continuation类型的参数,并将其添加到最后面。所以上面的协程真正的面目是这样的:

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

现在我们再来看上面的条件,发现能够全部符合了。

由于篇幅有限,有关协程的原理实现就点到为止,后续我会专门写一个协程系列,希望到时能够让读者们认识到协程的真面目,大家可以期待一下。

现在我们已经知道了Retrofit如何判断一个方法是否使用了协程。那么我们再进入另一个点:

Retrofit如何将Call直接转化为NewResonse,简单的说就是支持使newsGet方法返回NewsResponse。而这一步的转化在HttpServiceMethod中。

HttpServiceMethod

上面已经分析完RequestFactoryparseAnnotations(),现在再来看下HttpServiceMethod中的parseAnnotations()

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; // 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<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); Type responseType = callAdapter.responseType(); // 5. 创建Converter,将响应的数据转化成对应的model类型 Converter<ResponseBody, ResponseT> 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<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call>) callAdapter); } else { // 8. 接口方法是协程,同时返回类型是body,即自定义的model类型 //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call>) callAdapter, continuationBodyNullable); } }

代码中已经解析的很清楚了,需要注意3,如果是协程会做两步操作,首先将接口方法的返回类型伪装成Call类型,然后再将SkipCallbackExecutor手动添加到annotations中。字面意思就在后续调用callAdapter.adapt(call)时,跳过创建Executor,简单理解就是协程不需要Executor来切换线程的。为什么这样?这一点先放这里,后续创建Call的时候再说。

我们直接看协程的7,8部分。7也不详细分析,简单提一下,它就是返回一个Response<T>的类型,这个Retrofit最基本的支持了。至于如何在使用协程时将Call<T>转化成Response<T>原理与8基本相同,只是比8少一步,将它的body转化成对应的返回类型model。所以下面我们直接看8。

将Call转化成对应的Model

static final class SuspendForBody extends HttpServiceMethod<ResponseT, Object> { private final CallAdapter<ResponseT, Call> callAdapter; private final boolean isNullable;

SuspendForBody(RequestFactory requestFactory, okhttp3.Call.Factory callFactory, Converter<ResponseBody, ResponseT> responseConverter, CallAdapter<ResponseT, Call> 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<? extends Foo>"); } 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<Object, Call<?>>() { @Override public Type responseType() { return responseType; }

@Override public Call adapt(Call call) { // 3. 最终调用adapt时候返回的就是它本身的Call,即不需要进行适配。 return executor == null ? call : new ExecutorCallbackCall<>(executor, call); } }; }

代码注释已经将之前3的作用解释完了,我们回到SuspendForBodyadpt,再看第二步。熟悉的一幕,又用到了最后的一个参数。这里的isNullable目前Retrofit的版本都是false,可能后续会支持空类型。但现在肯定是不支持的,所以直接进入KotlinExtensions.await()

suspend fun Call.await(): T { return suspendCancellableCoroutine { continuation -> continuation.invokeOnCancellation { cancel() }

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

上面分享的百度、腾讯、网易、字节跳动、阿里等公司2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。

Android学习PDF+学习视频+面试文档+知识点笔记

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

【Android高级架构视频学习资源】

**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!