面试官:关于 Retrofit 你知道多少?(2),2024年最新最新高频安卓面试题目分享

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

2.2 定义 API 并获取 API 实例

public interface GitHubService {

@GET(“users/{user}/repos”)

Call<List> listRepos(@Path(“user”) String user);

}

GitHubService github = retrofit.create(GitHubService.class);

先看定义,非常简洁,也没有什么特别之处,除了两个注解:@GET和 @Path 。它们的用处稍后再分析,我们接着看创建 API 实 例: retrofit.create(GitHubService.class)。这样就创建了 API 实例了, 就可以调用 API 的方法发起 HTTP 网络请求了,太方便了。 但 create 方法是怎么创建 API 实例的呢?

public T create(final Class service) {

// 省略非关键代码

return (T) Proxy.newProxyInstance(service.getClassLoader(),

new Class<?>[] { service },

new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object … args)

throws Throwable {

// 先省略实现

}

});

}

创建 API 实例使用的是动态代理技术。

简而言之,就是动态生成接口的实现类(当然生成实现类有缓存机制),并创建其 实例(称之为代理),代理把对接口的调用转发给 InvocationHandler 实例, 而在 InvocationHandler 的实现中,除了执行真正的逻辑(例如再次转发给真 正的实现类对象),我们还可以进行一些有用的操作,例如统计执行时间、进行初 始化和清理、对接口调用进行检查等。 为什么要用动态代理?因为对接口的所有方法的调用都会集中转发到 InvocationHandler#invoke 函数中,我们可以集中进行处理,更方便了。你可 能会想,我也可以手写这样的代理类,把所有接口的调用都转发到 InvocationHandler#invoke 呀,当然可以,但是可靠地自动生成岂不更方便?

2.3 调用 API 方法

获取到 API 实例之后,调用方法和普通的代码没有任何区别:

Call<List> call = github.listRepos(“square”);

List repos = call.execute().body();

这两行代码就发出了 HTTP 请求,并把返回的数据转化为了 List<Repo>,太方 便了!

现在我们来看看调用 listRepos 是怎么发出 HTTP 请求的。上面 Retrofit#create 方法返回时省略的代码如下

return (T) Proxy.newProxyInstance(service.getClassLoader(),

new Class<?>[] { service },

new InvocationHandler() {

private final Platform platform = Platform.get();

@Override

public Object invoke(Object proxy, Method method, Object… . args)

throws Throwable {

// If the method is a method from Object then defer to n ormal invocation.

if (method.getDeclaringClass() == Object.class) {

return method.invoke(this, args);

}

if (platform.isDefaultMethod(method)) {

return platform.invokeDefaultMethod(method, service, p roxy, args);

}

ServiceMethod serviceMethod = loadServiceMethod(method);

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

return serviceMethod.callAdapter.adapt(okHttpCall);

}

});

如果调用的是 Object 的方法,例如 equals, toString,那就直接调用。 如果是default 方法(Java 8 引入),就调用 default 方法。这些我们都先不管,因 为我们在安卓平台调用 listRepos ,肯定不是这两种情况,那这次调用真正干活 的就是这三行代码了(好好记住这三行代码,因为接下来很长的篇幅都是在讲它们 😃 ):

ServiceMethod serviceMethod = loadServiceMethod(method);

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

return serviceMethod.callAdapter.adapt(okHttpCall);

在继续分析这三行代码之前,先看一个流程图

这三行代码基本就是对应于流程图中轴上部了, ServiceMethod , build OkHttpCall, CallAdapter adapt

2.4 ServiceMethod

ServiceMethod<T> 类的作用正如其 JavaDoc所言:

Adapts an invocation of an interface method into an HTTP call. 把对接口方法 的调用转为一次 HTTP 调用。

一个 ServiceMethod 对象对应于一个 API interface 的一个方 法, loadServiceMethod(method)方法负责加载 ServiceMethod :

ServiceMethod loadServiceMethod(Method method) {

ServiceMethod result;

synchronized (serviceMethodCache) {

result = serviceMethodCache.get(method);

if (result == null) {

result = new ServiceMethod.Builder(this, method).build();

serviceMethodCache.put(method, result);

}

}

return result;

}

这里实现了缓存逻辑,同一个 API 的同一个方法,只会创建一次。这里由于我们每 次获取 API 实例都是传入的 class 对象,而 class 对象是进程内单例的,所 以获取到它的同一个方法 Method 实例也是单例的,所以这里的缓存是有效的。

我们再看看 ServiceMethod 的构造函数:

ServiceMethod(Builder builder) {

this.callFactory = builder.retrofit.callFactory();

this.callAdapter = builder.callAdapter;

this.baseUrl = builder.retrofit.baseUrl();

this.responseConverter = builder.responseConverter;

this.httpMethod = builder.httpMethod;

this.relativeUrl = builder.relativeUrl;

this.headers = builder.headers;

this.contentType = builder.contentType;

this.hasBody = builder.hasBody;

this.isFormEncoded = builder.isFormEncoded;

this.isMultipart = builder.isMultipart;

this.parameterHandlers = builder.parameterHandlers;

}

成员很多,但这里我们重点关注四个成 员: callFactory, callAdapter , responseConverter 和 parameterHandlers 。

1.callFactory负责创建 HTTP 请求,HTTP 请求被抽象为了okhttp3.Call 类,它表示一个已经准备好,可以随时执行的 HTTP 请求;

2.callAdapter 把 retrofit2.Call<T>转为 T (注意和 okhttp3.Call 区分开来,retrofit2.Call<T>表示的是对一个 Retrofit 方法的调用),这个过程会发送一个 HTTP 请求,拿到服务器返回的数据(通 过 okhttp3.Call实现),并把数据转换为声明的 T 类型对象(通过 Converter<F, T> 实现);

3.responseConverter 是 Converter<ResponseBody, T> 类型,负责把服 务器返回的数据(JSON、XML、二进制或者其他格式,由 ResponseBody封装)转化为 T 类型的对象;

4.parameterHandlers 则负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数;

它们的使用稍后再分析,这里先看看它们的创建(代码比较分散,就不贴太多代码 了,大多是结论):

2.4.1 callFactory

this.callFactory = builder.retrofit.callFactory(),所以 callFactory 实际上由 Retrofit 类提供,而我们在构造 Retrofit 对象 时,可以指定 callFactory,如果不指定,将默认设置为一个 okhttp3.OkHttpClient

2.4.2 callAdapter

private CallAdapter<?> createCallAdapter() {

// 省略检查性代码

Annotation[] annotations = method.getAnnotations();

try {

return retrofit.callAdapter(returnType, annotations);

} catch (RuntimeException e) {

// Wide exception range because factories are user code.

throw methodError(e, “Unable to create call adapter for %s”, returnType);

}

}

可以看到, callAdapter 还是由 Retrofit 类提供。在 Retrofit 类内部, 将遍历一个 CallAdapter.Factory 列表,让工厂们提供,如果最终没有工厂能 (根据 returnType 和 annotations)提供需要的 CallAdapter,那将抛出 异常。而这个工厂列表我们可以在构造 Retrofit 对象时进行添加。

2.4.3, responseConverter

private Converter<ResponseBody, T> createResponseConverter() {

Annotation[] annotations = method.getAnnotations();

try {

return retrofit.responseBodyConverter(responseType, annotati ons);

} catch (RuntimeException e) {

// Wide exception range because factories are user code.

throw methodError(e, “Unable to create converter for %s”, re sponseType);

}

}

同样,responseConverter 还是由 Retrofit 类提供,而在其内部,逻辑和创 建 callAdapter 基本一致,通过遍历 Converter.Factory列表,看看有没有 工厂能够提供需要的 responseBodyConverter。工厂列表同样可以在构造 Retrofit 对象时进行添加。

2.4.4 parameterHandlers

每个参数都会有一个 ParameterHandler,由 ServiceMethod#parseParameter方法负责创建,其主要内容就是解析每个参数 使用的注解类型(诸如 Path , Query , Field 等),对每种类型进行单独的 处理。构造 HTTP 请求时,我们传递的参数都是字符串,那 Retrofit 是如何把我们 传递的各种参数都转化为 String 的呢?还是由 Retrofit 类提供 converter!

Converter.Factory 除了提供上一小节提到的 responseBodyConverter,还提 供 requestBodyConverter和 stringConverter,API 方法中除了 @Body 和 @Part 类型的参数,都利用 stringConverter 进行转换,而 @Body 和 @Part 类型的参数则利用 requestBodyConverter进行转换。

这三种 converter 都是通过“询问”工厂列表进行提供,而工厂列表我们可以在构造 Retrofit 对象时进行添加。

2.4.5 工厂让各个模块得以高度解耦

上面提到了三种工厂: okhttp3.Call.Factory, CallAdapter.Factory 和 Converter.Factory,分别负责提供不同的模块,至于怎么提供、提供何种模 块,统统交给工厂,Retrofit 完全不掺和,它只负责提供用于决策的信息,例如参 数/返回值类型、注解等。

这不正是我们苦苦追求的高内聚低耦合效果吗?解耦的第一步就是面向接口编程, 模块之间、类之间通过接口进行依赖,创建怎样的实例,则交给工厂负责,工厂同 样也是接口,添加(Retrofit doc 中使用 install 安装一词,非常贴切)怎样的工 厂,则在最初构造 Retrofit 对象时决定,各个模块之间完全解耦,每个模块只 专注于自己的职责,全都是套路,值得反复玩味、学习与模仿。

除了上面重点分析的这四个成员, ServiceMethod中还包含了 API 方法的 url 解 析等逻辑,包含了众多关于泛型和反射相关的代码,有类似需求的时候,也非常值 得学习模仿

2.5 OkHttpCall

终于把 ServiceMethod 看了个大概,接下来我们看看 OkHttpCall 。 OkHttpCall 实现了 retrofit2.Call ,我们通常会使用它的 execute()和 enqueue(Callback<T> callback)接口。前者用于同步执行 HTTP 请求,后者 用于异步执行。

2.5.1,先看 execute()

@Override

public Response execute() throws IOException {

okhttp3.Call call;

synchronized (this) {

// 省略部分检查代码

call = rawCall;

if (call == null) {

try {

call = rawCall = createRawCall();

} catch (IOException | RuntimeException e) {

creationFailure = e;

throw e;

}

}

}

return parseResponse(call.execute());

}

主要包括三步:

1.创建 okhttp3.Call ,包括构造参数;

2.执行网络请求;

3.解析网络请求返回的数据;

createRawCall() 函数中,我们调用了 serviceMethod.toRequest(args) 来创建 okhttp3.Request ,而在后者中,我们之前准备好的 parameterHandlers 就派上了用场。

然后我们再调用 serviceMethod.callFactory.newCall(request) 来创建 okhttp3.Call,这里之前准备好的 callFactory同样也派上了用场,由于工 厂在构造 Retrofit 对象时可以指定,所以我们也可以指定其他的工厂(例如使 用过时的 HttpURLConnection 的工厂),来使用其它的底层 HttpClient实现。

我们调用 okhttp3.Call#execute() 来执行网络请求,这个方法是阻塞的,执行 完毕之后将返回收到的响应数据。收到响应数据之后,我们进行了状态码的检查, 通过检查之后我们调用了 serviceMethod.toResponse(catchingBody) 来把响 应数据转化为了我们需要的数据类型对象。在 toResponse 函数中,我们之前准 备好的 responseConverter 也派上了用场。

好了,之前准备好的东西都派上了用场,还好没有白费 😃

2.5.2 再看 enqueue(Callback<T> callback)

这里的异步交给了 okhttp3.Call#enqueue(Callback responseCallback)来 实现,并在它的 callback 中调用 parseResponse 解析响应数据,并转发给传入 的 callback。

2.6 CallAdapter

终于到了最后一步了, CallAdapter<T>#adapt(Call<R> call)函数负责把 retrofit2.Call<R> 转为 T 。这里 T 当然可以就是 retrofit2.Call<R> ,这时我们直接返回参数就可以了,实际上这正是 DefaultCallAdapterFactory创建的 CallAdapter的行为。至于其他类型的 工厂返回的 CallAdapter 的行为,这里暂且不表,后面再单独分析。

至此,一次对 API 方法的调用是如何构造并发起网络请求、以及解析返回数据,这 整个过程大致是分析完毕了。对整个流程的概览非常重要,结合 stay 画的流程图, 应该能够比较轻松地看清整个流程了。

虽然我们还没分析完,不过也相当于到了万里长征的遵义,终于可以舒一口气了 😃

三、retrofit-adapters 模块

retrofit 模块内置了 DefaultCallAdapterFactory和 ExecutorCallAdapterFactory,它们都适用于 API 方法得到的类型为 retrofit2.Call 的情形,前者生产的 adapter 啥也不做,直接把参数返回,后 者生产的 adapter 则会在异步调用时在指定的 Executor 上执行回调。

retrofit-adapters 的各个子模块则实现了更多的工 厂: GuavaCallAdapterFactory , Java8CallAdapterFactory 和 RxJavaCallAdapterFactory 。这里我主要分析 RxJavaCallAdapterFactory,下面的内容就需要一些 RxJava 的知识了,不过 我想使用 Retrofit 的你,肯定也在使用RxJava 😃

RxJavaCallAdapterFactory#get方法中对返回值的类型进行了检查,只支持 rx.Single , rx.Completable 和 rx.Observable ,这里我主要关注对 rx.Observable 的支持。

RxJavaCallAdapterFactory#getCallAdapter 方法中对返回值的泛型类型进行 了进一步检查,例如我们声明的返回值类型为 Observable<List<Repo>>,泛型 类型就是 List<Repo> ,这里对 retrofit2.Response 和retrofit2.adapter.rxjava.Result 进行了特殊处理,有单独的 adapter 负责 进行转换,其他所有类型都由 SimpleCallAdapter负责转换。

总结

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的
还有高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。

跨平台开发:Flutter.png

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的
还有高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。**

[外链图片转存中…(img-5vtVt0az-1713182816187)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-2DVHejgV-1713182816187)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值