OkHttp与Retrofit的区别有哪些(okhttp,retrofit,移动开发)

时间:2024-04-19 22:20:42 作者 : 石家庄SEO 分类 : 移动开发
  • TAG :

    OkHttp%E4%B8%8ERetrofit%E7%9A%84%E5%8C%BA%E5%88%AB%E6%9C%89%E5%93%AA%E4%BA%9B

参考答案:
OkHttp和Retrofit都是目前流行网络开源框架

封装不同:
Retrofit封装了具体的请求,线程切换以及数据转换。
retrofit通过使用代理,外观,策略模式对okhttp进行了封装
OkHttp 是基于Http协议封装的一套请求客户端

职责不同:
Retrofit主要负责应用层面的封装,面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等。
OkHttp主要负责socket部分的优化与封装,比如网络访问,多路复用,buffer缓存,数据压缩等等。
OkHttp与Retrofit的区别有哪些

顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

Retrofit 可以说和 OkHttp 是亲兄弟了,它们都是由 Square 公司推出的网络请求库,并且 Retrofit 实际上是基于 OkHttp 实现的,它在 OkHttp 现有功能的基础上进行了封装,支持通过注解进行网络请求参数的配置,同时对数据返回后的解析、序列化进行了统一的包装,甚至在近期引入了对协程对支持。

今天就让我们一起来看看 Retrofit 是如何在 OkHttp 这样一个已经固定的框架的基础上,优雅的进行封装并拓展功能的。

基本使用
我们首先来看看 Retrofit 的基本使用,来对它有个大致的了解。

首先,我们可以构建一个如下的请求 Service 类,它里面对各个请求的方法及参数通过注解进行了标注:

之后,我们可以构建一个 Retrofit 对象,并通过 Retrofit.create 方法传入对应 class 从而构建对应的 Service 对象:

之后,我们调用 service 中对应的方法,就可以获取到 Call 对象了。

通过对 Call 对象调用 enqueue 就可以实现对请求的异步调用,而通过 execute 方法则可以实现请求的同步调用。

Retrofit 采用了 Builder 模式进行了构建,在 Builder 中可以进行非常多的配置,其中可以对 baseUrlokhttpClientconverterFactorycallAdapterFactory 等进行设置。

这里没什么特别的,都是一些简单的赋值,就不再关注了,我们只需要看看最后 Retrofit 被传入了哪些参数。它最后调用了下面这个构造函数对参数进行了初始化。

接着我们看到自己定义的 interface 是如何仅仅靠传递 classRetrofit.create 就能实现实例的获取的,它明明只是个接口呀?

可以看到,实际上 Service 对象的获取是通过动态代理实现的。这里首先通过 validateServiceInterface 方法对接口进行了检测,之后通过动态代理对该接口进行了代理。

对于 Object 类本身独有以及对应平台本身就存在的方法,就照常调用,否则通过 loadServiceMethodService 中对应的 Method 对象进行处理,之后对其调用 invoke 方法。

这里说明了 Retrofit 不是在创建 Service 接口对应对象时就立即对所有该接口中的所有方法都进行注解的解析,而是采用了在方法被调用时才进行注解的解析这种懒加载的思想。

接着我们看看 validateServiceInterface 方法:

首先,这个方法对 service 进行了检测,保证了它是一个接口并且它和它继承的类中没有范型参数。

之后如果在 Retrofit 创建时设置 validateEagerly 为 true 的话,会对 Service 中所有非平台独有且非static的方法通过 loadServiceMethod 方法提前进行处理

首先它会采用 Double Check 的方式尝试从 serviceMethodCache 缓存中获取 ServiceMethod 对象,如果获取不到则通过 ServiceMethod.parseAnnotations 方法对该 Method 的注解进行处理并将得到的 ServiceMethod 对象加入了缓存。

也就是说为了避免多次对方法的注解进行处理,Retrofit 采用了一个 serviceMethodCache 对解析后的 ServiceMethod 进行缓存。

接着我们就来看看,parseAnnotations 方法是如何对方法的注解进行解析的。

这里先通过 RequestFactory.parseAnnotations 方法对注解解析并获得了一个 RequestFactory 对象。

之后又通过 HttpServiceMethod.parseAnnotations 方法传入了 requestFactory继续进行注解的解析并获得 ServiceMethod 对象

我们先看看 RequestFactory.parseAnnotations

它把 Method 传入 Builder 从而构建了一个新的 RequestFactory

Builder 中通过反射获取到method所包含的注解、参数包含的范型以及参数的注解。

接着看看 build 方法:

在 build 方法中主要是对方法的每个注解调用了 parseMethodAnnotation 进行了解析,并且对每个参数调用了 parseParamter 方法解析为了 ParamterHandler 对象。

parseMethodAnnotation 的代码如下:

这里实际上就是对每一种 HTTP 所支持的类型进行了支持,获取到了对应注解的中的 url,并调用parseHttpMethodAndPath 进行处理,同时对 Headers 注解则是通过 parseHeaders 进行了处理。

对于 Method 和 Path,通过 parseHttpMethodAndPath 进行了参数的赋值:

这里实际上就是对不同 HTTP 请求方式和 Path 进行了赋值,同时通过正则表达式保证了这个接口的 Path 中没有包含参数。

而对于 Headers 则是将传递进来的 Headers 列表解析为了对应的 Headers 对象。

parseParamterAnnotation 方法的代码太长了,这里就不再贴了,它对方法的每个注解都进行了独有的处理,并返回了对应的 ParamterHandler

可以发现,RequestFactory.parseAnnotations 的主要作用就是完成对方法注解信息的解析,从而用于产生对应的 Request

这里的代码非常非常长,大致可归纳为下面的步骤:

1. 如果这个方法是 Kotlin 中的 suspend 方法,由于由协程实现,因此需要获取 Continuation 的范型参数,这个参数就是请求返回值的真正类型。
2. 如果 suspend 方法返回值是 Response,则说明它需要的是 Response 而不是具体的类型,那么将 continuationWantsResponse 置为 true;
3. 如果不是 suspend 方法,则返回值的范型参数的类型就是请求返回值的真正类型(Call<ReturnType>ReturnType 才是真正经过转换后需要的类型)。
4. 通过 createCallAdapter 方法创建 CallAdapter 对象,它是用于将 Call<ResponseT> 对象适配为需要的类型 ReturnT 对象的。
5. 拿到 CallAdapter 后,获取到了 Response 的类型,并进行了校验。
6. 通过 createResponseConverter 方法获取 Converter 对象,它可以完成从 ResponseBodyResponse 类型 ResponseT 的转换。
7. 如果并非 Kotlin 的 suspend 方法,则直接传入 CallAdapter 及 Converter,创建 CallAdapted 对象。
8. 否则根据 suspend 方法需要的是 Response 还是具体的类型,分别返回 SuspendForResponseSuspendForBody 对象。

可以发现,新版的 Retrofit 对 Kotlin 的协程进行了支持。HttpServiceMethod.parseAnnotations 的主要作用就是创建 CallAdapter 以及 Converter 对象,并构建对应 HttpServiceMethod

CallAdapter 是用于将Call<R>对象适配为需要的类型 T 对象的。它的声明如下:

我们先看看 createCallAdapter 方法是如何对它创建的:

它调用了 retrofit.callAdapter 方法:

之后调用到 retrofit.nextCallAdapter 方法:

这里实际上是遍历了创建 Retrofit 对象时传递的 CallAdapter.Factory 列表尝试去创建 CallAdapter。如果这些 CallAdapter.Factory 都无法处理这个对应的 returnType 以及 annotations 的话,则会抛出异常。(前面 Factory 的优先级更高)

Retrofit 中有一个默认的 CallAdapter 工厂 DefaultCallAdapterFactory它的优先级比所有自定义工厂要低,它在创建时会传入一个 Executor,我们可以看到它的 get 方法:

可以看到,在没有 Executor 时,它不对 Call 进行修改,在有指定 Executor 时,则会将其包装为 ExecutorCallbackCall。一般来说这个 Executor 就是创建 Retrofit 时指定的 callbackExecutor

这个 callbackExecutor 实际上是用来指定调用 Callback 的线程的,从而使得 Callback 并不一定是在主线程被回调:

可以看到,这里实际上只是对 Callback 进行了包装,通过传递的 Executor 进行回调,从而对 callbackExecutor 进行支持。

接着我们看看 Converter 类,它是一个接口,用于将类型 F 的数据转换为类型 T:

接着我们看看 createResponseConverter 是如何对它进行创建的:

转调到了 retrofit.responseBodyConverter

转调到了 nextResponseBodyConverter

可以看到,这里与 CallAdapter 工厂类似,遍历创建 Retrofit 时传入的 Converter.Factory 列表,尝试进行创建,如果没有工厂能对其进行处理,抛出异常。(前面 Factory 的优先级更高)

Retrofit 中内置了两个 Converter.Factory,分别是 BuiltInConverters 以及 OptionalConverterFactory

其中 BuiltInConverters 的优先级比所有自定义工厂要高,以避免其他工厂覆盖它的方法,而 OptionalConverterFactory的优先级比所有自定义工厂的优先级更低。

BuiltInConverters 中实现了多个转换器如将 ResponseBody 转换为 Void 或 Unit,将 Object 转换为 String 等。

OptionalConverterFactory是通过 platform 获取到的 defaultConverterFactories,它是为了支持 Java 8 的 Optional 而实现的,Optional 是 Java 8 引入的用来解决空指针异常的类。

接着我们看看之前创建的 ServiceMethod 类,它是一个抽象类,需要子类对 invoke 方法进行实现。

它的子类就是前面提到的 HttpServiceMethod

HttpServiceMethod 的 invoke 方法非常简单,它构造了一个 OkHttpCall,然后通过 adapt 这个虚函数来实现对 Call 的转换。它的子类只需要实现 adapt 从而对 Call 进行转换即可。

它共有三个子类,首先就是并非使用协程的情况下的 CallAdapted 类,另外两个子类则是在使用协程的情况下为了配合协程的 SuspendForResponse 以及 SuspendForBody

CallAdapted 类继承自 HttpServiceMethod 类,并通过传递进来的 CallAdapter 对 Call 进行了转换。

SuspendForResponse 类首先根据传递进来的 Call 构造了一个参数为 Response<ResponseT>Continuation 对象然后通过 Kotlin 实现的 awaitResponse 方法将 call 的 enqueue 异步回调过程封装为了一个 suspend 的函数。

awaitResponse 方法如下:

可以看到,分别通过在 onResponseonFailure 中调用 continuation.resumecontinuation.resumeWithException从而对协程进行支持。

SuspendForBody 则是根据传递进来的 Call 构造了一个 Continuation<ResponseT> 对象然后通过 Kotlin 实现的 await 或 awaitNullable 方法将 call 的 enqueue 异步回调过程封装为了一个 suspend 的函数。

Call 实际上是一个接口,它提供了 executeenqueuecancel 等接口用于实现请求,当我们需要请求一个接口的时候,只需要调用其 enqueueexecute方法即可。

从前面的过程中我们可以了解到,如果我们没有传入 CalAdapter 的话,默认情况下返回的 Call 实际上是 OkHttpCall 对象,让我们通过它来看看 Retrofit 如何基于 OkHttp 实现的网络请求:

首先让我们看看 enqueue 的代码:

enqueue 的代码看似多,实际上比较简单,主要分为以下几步:

1. 加锁,对执行状态进行设置,若不存在 rawCall 则调用 createRawCall 方法创建 okhttp3.Call 对象。
2. 如果外界取消该任务,则调用 okhttp3.Call.cancel
3. 通过 okhttp3.Call.enqueue 将消息入队
4. 若获得 Response,则通过 parseResponse 方法对 Response 进行解析,解析完成后通过 onResponse 回调解析结果。
5. 若请求失败,通过 callFailure 方法调用 onFailure 回调请求失败。

可以发现一个小细节,Retrofit 对已经创建的 okhttp3.Call 进行了复用,避免了重复创建从而浪费效率。

接着让我们看看 execute 是如何实现的:

也非常简单:

1.首先加锁后对执行状态进行设置,若不存在 rawCall 则调用 createRawCall方法创建 okhttp3.Call 对象。
2.如果外界取消该任务,则调用 okhttp3.Call.cancel
3.若获得 Response,则通过 parseResponse 方法对 Response 进行解析并返回

接着让我们看看 createRawCall 方法:

它实际上是调用了 callFactorynewCall 方法进行创建,而传入的 okhttp3.Request 则是通过 requestFactory.create 创建的:

这里首先构建了一个 RequestBuilder,之后通过遍历 ParamterHandler 列表并调用其 apply 方法将参数应用到 RequestBuilder 中。

接着我们看看 parseResponse 是如何对 Response 进行解析的:

可以看到,它会通过 Converter.convert 方法将 Response 的 body 转换为我们所需要的类型。

那么到这里,Retrofit的源码我们就基本上看完了,可以发现它的代码中基本很少涉及到对网络请求的处理,基本都是对于 OkHttp 的封装,在 OkHttp 现有的 API 上提供了一套更便于使用的框架。

Retrofit 很好地向我们证明了,如何在一套已经固定的 API 上,以最优雅的方式对现有的框架进行封装,拓展出更为强大的功能。

Retrofit 的注解是一种运行时注解,它通过动态代理对 Service 对象进行创建,通过反射对注解进行解析,这样虽然会有一定性能的损耗,但性能的损耗却带来了十分易用的 API。用户所写的 Service 接口中,每一个 ServiceMethod都对应了一个接口。

对于 Service 的创建,它是通过动态代理,对每个接口内定义的方法进行了代理,若设置 validateEagerly了则会在创建 Service 接口对象时进行注解的解析以及 ServiceMethod 的创建,否则会在方法调用时再创建对应的 ServiceMethod 对象,在多次调用的情况下,它通过 serviceMethodCache 对已经解析的 ServiceMethod 进行了缓存从而避免了重复解析带来的性能损耗。

这个对象的创建首先会经过 RequestFactory.parseAnnotations 对方法中的注解进行解析:

对于方法的请求方式注解,它会通过 parseHttpMethodAndPath 方法获取注解对应的请求方式以及注解中的 url 等信息并保存起来,在创建请求的时候设置进 RequestBuilder

对于方法的 Headers 注解,它会将 Header 注解解析为 Headers 对象并保存起来,在创建请求的时候设置进 RequestBuilder

对于方法的参数,它会将每个参数根据具体的注解(@Query 等)解析为对应的 ParamterHandler,在创建请求的时候,会通过它的 apply 方法将参数提交到 RequestBuilder 中。

之后,这个对象会通过 HttpServiceMethod.parseAnnotationsServiceMethod 对象进行创建,它在创建的过程中同时进行了接口对应的 CallAdapter 以及 Converter 的创建。

其中,CallAdapter 用于将Call<R> 对象适配为需要的类型 T 对象,也就是对 Call 进行转换。

Converter 则是用于将 F 类型的数据转换为 T,往往是用于对 Response的 body 进行转换。

对于 CallAdapterConverter 都是通过它们对应的工厂类进行创建,创建时会根据工厂列表的顺序从前向后尝试进行创建,也就是说在工厂列表中越靠前的工厂其优先级越大。

同时,Retrofit 还引入了对 Continuation 协程的支持,它会将 ServerMethod 最终包装为一个 suspend 方法从而对协程进行支持。

Retrofit的网络请求的执行依赖于 OkHttp,它首先会通过 RequestFactory 进行 Request 的构造,它的参数通过前面解析的信息得来。之后会将这个 Request 包装为一个 okhttp3.Call,在同步和异步请求时分别调用其对应的 execute 及 enqueue 方法。同时,为了避免okhttp3.Call 的重复创建,它对之前创建的 okhttp3.Call 进行了复用。

本文:OkHttp与Retrofit的区别有哪些的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:9102年底将至,BAT高级开发21道汇总:Bitmap+H下一篇:

5 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18