Retrofit2 源码深度分析(一)

本文来自饿了么大牛RichardCao投稿,文章的质量还是非常不错的,同时也希望更多的朋友可以多多投稿,可以把更多技术干货分享给大家。Retrofit是非常优秀的网络请求框架。本文将顺着构建请求对象→构建请求接口→发起同步/异步请求的流程,分析retrofit2是如何实现的。

RichardCao的博客地址:

http://richardcao.me/

1.组成部分

Retrofit2源码主要分为以下几个部分:

  • retrofit

  • retrofit-adapters

  • retrofit-converters

本篇先分析retrofit部分,也就是retrofit源码的主干部分。下面顺着retrofit2的使用流程进行分析。

2.Retrofit对象的构建

首先我们看看构建一个Retrofit对象,都需要或者可选哪些配置:

《Retrofit2 源码深度分析(一)》        Retrofit对象的构建使用了建造者模式,这里有一系列参数可以供我们选择,我给这些参数加了注释。这里构造方法中Platform.get()就是获取当前使用retrofit的平台信息,之前我用retrofit的时候,以为只支持Android平台,没想到还支持java8和ios,只不过这里的ios是指通过robovm平台构建的ios程序,目前robovm主要的成功案例还是游戏,跨android & ios双平台。在Builder中,有一个比较重要的配置项,就是baseURL。我们设置baseUrl就是一个string字符串,retrofit是这么处理的:

《Retrofit2 源码深度分析(一)》

《Retrofit2 源码深度分析(一)》

        这里的注释非常详细,讲解了baseUrl到底是什么,应该遵循什么样的格式,然后经过HttpUrl的解析,组合成okhttp3用来请求的url链接。这里规定了baseUrl末尾应该以/符号结尾,在后续API的接口类中后半部分定义应该不以/开头,这是和retrofit 1.x版本不同的地方。最后就是build方法了:

《Retrofit2 源码深度分析(一)》        从这里可以看出如果我们不设置callFactory,retrofit会默认帮我们new一个OkHttpClient,如果我们不设置callbackExecutor,也会帮我们默认获取到当前平台默认的callbackExecutor,最后new一个Retrofit对象,到这里,Retrofit对象的构建就讲完了。这里有个值得注意的地方:CallAdapter和Converter的工厂集合都使用了保护性拷贝。那么保护性拷贝是什么呢?这是用来保证代码健壮性的。为什么要在这里用?因为Builder的这些配置的方法都是public的,虽然看起来这些是不可变的,但是可以通过传入构造参数来修改引用的值,这就会造成约束条件被破坏,所以使用了保护性拷贝来防止这种情况。

3.API的编写

我们已经new好了一个我们需要的Retrofit对象,那么下一步就是编写API了。如何编写API呢?Retrofit的方式是用过java interface和注解的方式进行定义。例如:

《Retrofit2 源码深度分析(一)》这是官方文档上的一个例子。这里简单的定义了一个API,针对这小部分代码,我们来分析分析。
首先是GET注解,我们来看看这个注解是什么:

《Retrofit2 源码深度分析(一)》        这是一个运行时的方法注解,用来构造get请求,唯一的参数是一个string值,默认是空字符串。那么我们可以理解了,@GET(xxxxx)就是构造了一个用于get请求的url。下一个注解是Path注解,是一个运行时的参数注解,它是为了方便我们构建动态的url,参数是一个string值,还可以设置参数是否已经是URL encode编码,默认是false。最后我们看到,通过Call 构建成一个interface。Call 这个接口分别在OkHttpCallExecutorCallbackCall中做了具体的实现。

4.创建Retrofit Service

最最关键的一步来了。我们new好了retrofit对象,也写好了API interface,那怎么请求这个API呢,我们需要通过retrofit的create函数,创建一个service,然后调用API的接口方法进行请求并获得回传。使用当然很简单:

《Retrofit2 源码深度分析(一)》是的你没有看错,就这么一句话就搞定了。具体是怎么实现的呢?我们先来看看create的代码:

《Retrofit2 源码深度分析(一)》         这里实现的非常巧妙,使用了java的动态代理。什么是动态代理?就是代理类、委托类都要实现同一个接口,然后代理通过接口代理委托类。由于java的单继承特性,所以动态代理是基于接口的,只能针对接口创建代理类。所以这里create的时候,先判断了一下传进来的service是不是interface,如果不是接口类型或者包含多个接口,就会直接抛异常。然后就是重头戏:动态代理的invoke方法。这个方法是InvocationHandler接口中唯一的方法,这个接口是代理实例实现的接口。代理实例调用方法时,InvocationHandler接口会将对方法的调用指派到代理的invoke方法中,进行处理。这里当method是一个对象的method时,直接调用。如果是平台的默认方法(根据Platform代码中这种情况是java8)就直接执行调用默认方法。正常在android平台下,会把method加载到ServiceMethod对象中,这里用了缓存,如果缓存中有SeriveMethod就直接取出,如果没有接new一个,然后用过ServiceMethod初始化了一个OkHttpCall对象,最后通过callAdapteradapt方法返回一个代理了Call的实例。这个方法在这两个类中有具体的实现:
DefaultCallAdapterFactoryExecutorCallAdapterFactory。这两个工厂类又在哪里会用到?就在Retrofit对象的build方法中:

《Retrofit2 源码深度分析(一)》那我们再看看platform.defaultCallAdapterFactory(callbackExecutor)里是什么《Retrofit2 源码深度分析(一)》可以看到如果在创建Retrofit对象时callbackExecutor为空的话,就new一个ExecutorCallAdapterFactory对象作为CallAdapter,如果不为空就返回DefaultCallAdapterFactory的实例。

5.发起请求

这里我们按照retrofit官方文档中的例子构建Retrofit对象。
retrofit2中,同步和异步不再用interface的定义区分,统一都为Call ,但是在Call接口的方法中有区分,我们来看看:

《Retrofit2 源码深度分析(一)》execute()是同步请求,enqueue()是异步请求。我们先看异步请求是如何实现的。enqueue这个方法在以下两个地方有实现:
OkHttpCallExecutorCallbackCall。那么当发起了异步请求之后,就会调用ExecutorCallbackCall中的enqueue方法,ExecutorCallbackCall中一个对象叫

《Retrofit2 源码深度分析(一)》这就是代理的Call对象,在这里就是我们在动态代理create方法中用过ServiceMethod对象构造的OkHttpCall对象,那么这里调用的enqueue方法,就是调用了代理的OkHttpCall对象的enqueue方法:

《Retrofit2 源码深度分析(一)》    可以看到,这里其实是用了okhttp3的请求回调,做了一层封装,变成了retrofit2的Callback,也可以看出retrofit2和okhttp3是深度集成的。到这里,异步请求就一目了然了。我们还可以发现,retrofit2支持的可以取消请求,其实用的就是okhttp3的cancel方法。

       同理,同步请求也是一样的,也是调用了代理的OkHttpCall的方法,只不过是execute方法,这个方法里面没有回调,和异步不同,是直接返回解析好的Response对象的,这就是同步请求啦。

至此,顺着构建请求对象→构建请求接口→发起同步/异步请求这个流程,我们分析了一遍retrofit2到底是如何实现的。

6.最后再说几句
  1. retrofit2主模块源码其实并不是很多,我感觉用的最巧妙的就是create方法的动态代理,然后加上运行时注解来构建API,深度结合okhttp3,使得网络请求的构建变得非常简洁,并且功能强大,而且安全。

  2. retrofit2同时支持与rxjava配合使用,是通过设置adapter来实现的,retrofit2把adapters和converters从主代码里拆分出来了,相当于组件化的意思,如果需要使用,就由开发者自己引用并定义,这种组件化的思想我觉得也非常棒。

  3. retrofit2源码里是通过junit来写测试的,测试代码写的也非常好,更说明了优秀的代码离不开优秀的测试,这也是值得我们学习的地方。

《Retrofit2 源码深度分析(一)》

    原文作者:java集合源码分析
    原文地址: https://juejin.im/entry/574b73575bbb500057a35851
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞