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

最后

看完美团、字节、腾讯这三家的面试问题,是不是感觉问的特别多,可能咱们又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。

开篇有提及我可是足足背下了1000道题目,多少还是有点用的呢,我看了下,上面这些问题大部分都能从我背的题里找到的,所以今天给大家分享一下互联网工程师必备的面试1000题

注意不论是我说的互联网面试1000题,还是后面提及的算法与数据结构、设计模式以及更多的Java学习笔记等,皆可分享给各位朋友

最新“美团+字节+腾讯”一二三面问题,挑战一下你能走到哪一面?

互联网工程师必备的面试1000题

而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题

最新“美团+字节+腾讯”一二三面问题,挑战一下你能走到哪一面?

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

相信老鸟都应该很清楚,Retrofit核心部分是create()方法返回的动态代理(这里就不详细说明了,之后会有专门的文章分析动态代理)。就是下面这段代码:

public T create(final Class service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];

@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}

第一眼看,跟我之前印象中的有点区别(也不知道是什么版本),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<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);
}
}

总结

阿里伤透我心,疯狂复习刷题,终于喜提offer 哈哈~好啦,不闲扯了

image

1、JAVA面试核心知识整理(PDF):包含JVMJAVA集合JAVA多线程并发,JAVA基础,Spring原理微服务,Netty与RPC,网络,日志,ZookeeperKafkaRabbitMQ,Hbase,MongoDB,Cassandra,设计模式负载均衡数据库一致性哈希JAVA算法数据结构,加密算法,分布式缓存,Hadoop,Spark,Storm,YARN,机器学习,云计算共30个章节。

image

2、Redis学习笔记及学习思维脑图

image

3、数据面试必备20题+数据库性能优化的21个最佳实践

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

库性能优化的21个最佳实践

[外链图片转存中…(img-ZFSeyQvT-1715512187115)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 26
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值