Android _《看完不忘系列》之Retrofit,android面试题及答案2019

//运行期生成一个实现WanApi接口的类(字节码),并反射创建其实例
WanApi wanApi = retrofit.create(WanApi.class);
//得到Retrofit的call,他封装了okhttp的call
Call call = wanApi.articleList(0);
//请求入队
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
//得到数据实体
WanArticleBean bean = response.body();
//不同于okhttp,Retrofit已经用Handler帮我们切回主线程了
mBinding.tvResult.setText("" + bean.getData().getDatas().size());
}

@Override
public void onFailure(Call call, Throwable t) {}
});
}
}

实现原理

Android _《看完不忘系列》之Retrofit,android面试题及答案2019_第1张图片

由于Retrofit底层基于okhttp,哈迪在《看完不忘系列》之okhttp已经对网络流程做了分析,所以本文忽略网络实现只关注Retrofit自身的一些处理,Retrofit对象的构建就是简单的builder模式,我们直接看create,

//Retrofit.java
public T create(final Class service) {
//验证
validateServiceInterface(service);
return (T)
//动态代理
Proxy.newProxyInstance(
service.getClassLoader(), //类加载器
new Class[] {service}, //一组接口
new InvocationHandler() {
//判断android和jvm平台及其版本
private final Platform platform = Platform.get();

@Override
public Object invoke(Object proxy, Method method, Object[] args){
//如果该方法是Object的方法,直接执行不用管
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//isDefaultMethod:检查是否是java8开始支持的接口默认方法
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args); //我们关注这里
}
});
}

Proxy.newProxyInstance动态代理,运行期会生成一个类(字节码)如$ProxyN,实现传入的接口即WanApi,重写接口方法然后转发给InvocationHandler的invoke,如下(伪代码),

class $ProxyN extends Proxy implements WanApi{
Call articleList(@Path(“page”) int page){
//转发给invocationHandler
invocationHandler.invoke(this,method,args);
}
}

我们先看validateServiceInterface验证逻辑,

//Retrofit.java
private void validateServiceInterface(Class service) {
//检查:WanApi不是接口就抛异常…
//检查:WanApi不能有泛型参数,不能实现其他接口…
if (validateEagerly) { //是否进行严格检查,默认关闭
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) { //遍历WanApi方法
//不是默认方法,并且不是静态方法
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
//把方法提前加载进来(检查下有没有问题)
loadServiceMethod(method);
}
}
}
}

如果开了validateEagerly,会一次性把接口WanApi的所有方法都检查一遍并加载进来,可以在debug模式下开启,提前发现错误写法,比如在@GET请求设置了@Body这种错误就会抛出异常:

java.lang.IllegalArgumentException: Non-body HTTP method cannot contain @Body.

loadServiceMethod

然后是loadServiceMethod(method).invoke(args),看名字可知是先找方法,然后执行,

//Retrofit.java
//缓存,用了线程安全ConcurrentHashMap
final Map> serviceMethodCache = new ConcurrentHashMap<>();

ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result = serviceMethodCache.get(method);
//WanApi的articleList方法已缓存,直接返回
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
//解析articleList的注解,创建ServiceMethod并缓存起来
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}

跟进ServiceMethod.parseAnnotations,

//ServiceMethod.java
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
//1.
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
//检查:articleList方法返回类型不能用通配符和void…
//2.
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

先看1. RequestFactory.parseAnnotations,

//RequestFactory.java
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}

class Builder {
RequestFactory build() {
//解析方法注解如GET
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
//省略各种检查…
//解析参数注解如Path
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
//省略各种检查…
return new RequestFactory(this);
}
}

得到RequestFactory后,看2. HttpServiceMethod.parseAnnotations,HttpServiceMethod负责适配和转换处理,将接口方法的调用调整为HTTP调用,

//HttpServiceMethod.java
//ResponseT响应类型如WanArticleBean,ReturnT返回类型如Call
static HttpServiceMethod parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
//省略kotlin协程逻辑…
Annotation[] annotations = method.getAnnotations();
//遍历找到合适的适配器
CallAdapter callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
//得到响应类型,如WanArticleBean
Type responseType = callAdapter.responseType();
//遍历找到合适的转换器
Converter responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}

可见最终返回了一个CallAdapted,看到CallAdapted,

//CallAdapted extends HttpServiceMethod extends ServiceMethod
class CallAdapted extends HttpServiceMethod {
private final CallAdapter callAdapter;

CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter responseConverter,
CallAdapter callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}

@Override
protected ReturnT adapt(Call call, Object[] args) {
//适配器
return callAdapter.adapt(call);
}
}

那这个CallAdapter实例到底是谁呢,我们先回到Retrofit.Builder,

//Retrofit.Builder.java
public Retrofit build() {
Executor callbackExecutor = this.callbackExecutor;
//如果没设置线程池,则给android平台设置一个默认的MainThreadExecutor(用Handler将回调切回主线程)
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
//添加默认的DefaultCallAdapterFactory
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
}

DefaultCallAdapterFactory这个工厂创建具体的CallAdapter实例,

//DefaultCallAdapterFactory.java
public CallAdapter get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
//如果指定了SkipCallbackExecutor注解,就表示不需要切回主线程
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter>() {
@Override
public Type responseType() {
return responseType;
}

@Override
public Call adapt(Call call) {
//默认情况下,返回用主线程池包装的Call,他的enqueue会使用主线程池的execute
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}

invoke

前边loadServiceMethod得到了CallAdapted,然后执行invoke,实现在父类HttpServiceMethod里,

//HttpServiceMethod.java
final ReturnT invoke(Object[] args) {
//终于见到okhttp了!
Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}

class CallAdapted extends HttpServiceMethod {
private final CallAdapter callAdapter;

@Override
protected ReturnT adapt(Call call, Object[] args) {
//用前边得到的适配器,把OkHttpCall包成ExecutorCallbackCall
return callAdapter.adapt(call);
}
}

然后是请求入队,ExecutorCallbackCall.enqueue -> OkHttpCall.enqueue,

//ExecutorCallbackCall.java
void enqueue(final Callback callback) {
delegate.enqueue(
new Callback() {
@Override
public void onResponse(Call call, final Response response) {
//将回调切回主线程
callbackExecutor.execute(
() -> {
callback.onResponse(ExecutorCallbackCall.this, response);
});
//…
}

@Override

最后

简历首选内推方式,速度快,效率高啊!然后可以在拉钩,boss,脉脉,大街上看看。简历上写道熟悉什么技术就一定要去熟悉它,不然被问到不会很尴尬!做过什么项目,即使项目体量不大,但也一定要熟悉实现原理!不是你负责的部分,也可以看看同事是怎么实现的,换你来做你会怎么做?做过什么,会什么是广度问题,取决于项目内容。但做过什么,达到怎样一个境界,这是深度问题,和个人学习能力和解决问题的态度有关了。大公司看深度,小公司看广度。大公司面试你会的,小公司面试他们用到的你会不会,也就是岗位匹配度。

面试过程一定要有礼貌!即使你觉得面试官不尊重你,经常打断你的讲解,或者你觉得他不如你,问的问题缺乏专业水平,你也一定要尊重他,谁叫现在是他选择你,等你拿到offer后就是你选择他了。

另外,描述问题一定要慢!不要一下子讲一大堆,慢显得你沉稳、自信,而且你还有时间反应思路接下来怎么讲更好。现在开发过多依赖ide,所以会有个弊端,当我们在面试讲解很容易不知道某个方法怎么读,这是一个硬伤…所以一定要对常见的关键性的类名、方法名、关键字读准,有些面试官不耐烦会说“你到底说的是哪个?”这时我们会容易乱了阵脚。正确的发音+沉稳的描述+好听的嗓音决对是一个加分项!

最重要的是心态!心态!心态!重要事情说三遍!面试时间很短,在短时间内对方要摸清你的底子还是比较不现实的,所以,有时也是看眼缘,这还是个看脸的时代。

希望大家都能找到合适自己满意的工作!
如果需要PDF版本可以在GitHub中自行领取!

  • 或者点击这里自行下载,直达领取链接

进阶学习视频

Android _《看完不忘系列》之Retrofit,android面试题及答案2019_第2张图片

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

Android _《看完不忘系列》之Retrofit,android面试题及答案2019_第3张图片

%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)**

进阶学习视频

[外链图片转存中…(img-bRQvhU1g-1645093811559)]

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-zbYfQc6S-1645093811559)]

你可能感兴趣的:(程序员,面试,移动开发,android)