知识点汇总:
一:Retrofit项目介绍与实现网络请求原理
二:Okhttp的项目介绍与实现网络请求的原理
三:项目中使用到的设计模式场景分析
四:项目中的核心类分析
五:常见问题汇总
六:扩展阅读
一:Retrofit项目介绍与实现网络请求原理
官方简介:Retrofit是您的API接口转换为可调用对象的类,默认情况下,Retrofit将为您的平台提供合理的默认设置,但它也允许自定义。
项目结构图:
retrofit:项目的具体实现源码和自定义的网络请求相关注解。
retrofit-adapters:Retrofit附带一个用于执行调用实例的默认适配器,这里包含的子模块是其他流行执行机制的附加适配器,要使用请在构建改造实例时提供所需适配器的实例。
retrofit-converters:Retrofit附带了对OkHttp的RequestBody和ResponseBody类型的支持,但该库与内容格式无关,这里包含的子模块是其他流行格式的附加转换器,要使用请在构建改造实例时提供所需转换器的实例。
samples:项目的接口调用示例代码。(包含12个示例代码)
Retrofit实现网络请求解析:
第一步:通过建造者模式创建复杂的对象Retrofit类的示例,这里的build函数通过建造者模式中链式调用把相关传入的参数来初始化Retrofit的内部变量,如果链式调用中没有设置相关参数,build函数里面也会设置相关的默认实现的参数,执行默认操作实现。
下面我们通过一个官方提供的Demo示例,查看如何通过Retrofit实现一次简单的网络请求,代码如下:
自定义网络请求接口类:
Retrofit类成员变量:
函数解析:
1、Map<Method, ServiceMethod<?>> serviceMethodCache:这是一个方法的缓存类,key为网络请求的Method,比如GET,POST等,而ServiceMethod则对应着动态代理解析后的方法类。
2、okhttp3.Call.Factory callFactory:这个是创建OkHttp的工厂类。
3、HttpUrl baseUrl:这个是基础URL,网络请求会带上这个基础URL。
4、List<Converter.Factory> converterFactories:Converter.Factory的集合,Converter.Factory是将返回的数据通过这个工厂转化为对应的数据,比如Gson的GsonConverterFactory工厂类,也就是数据转化器工厂。
5、List<CallAdapter.Factory> callAdapterFactories:CallAdapter.Factory的集合,CallAdapter.Factory是网络请求的适配器工厂,比如把Call转化为RxJava请求的RxJavaCallAdapterFactory工厂,也就是Call转化工厂。
6、Executor callbackExecutor:用于回调网络请求。
7、boolean validateEagerly:用于判断是否需要立即解析方法。
build函数实现:(删除部分代码)
这里设置建造者模式链式调用传入的相关参数,如果没有传入,则设置默认实现参数,其中还包含部分额外的初始操作,执行完该函数,Retrofit类对象就创建成功了。
第二步:自定义接口类Github通过接口传入create()函数中,在create函数中首先会检查自定义接口函数是否符合要求(不支持泛型、静态函数等),不符合则抛出异常,接着,会通过Proxy类创建代理类,在代理类的invoke函数根据不同的平台(Android、java等)把接口类中定义的请求函数(Mathod),转换为具体的请求对象HttpServiceMethod,并缓存到ConcurrentHashMap数据结构中。
下面看看校验服务接口的实现代码:(不符合条件的接口类就抛出异常)
接着我们看看自定义的接口中的请求函数,是如何生成一个一个的网络请求对象的(HttpServiceMethod),代码如下:
第三步:最后通过github.contributors("square", "retrofit")和call.execute()函数执行网络请求,实际上会触发上面动态代理类中的invoke函数执行,最终通过反射类Method的invoke函数,触发HttpServiceMethod的invoke函数执行,而通过查看invoke的实现,最终通过execute()函数执行的网络请求动作是通过Okhttp实现的,所以说Retrofit只是对Okhttp进行封装实现。
OkHttpCall类的execute()函数实现,通过okhttp实现网络请求:
Retrofit网络请求流程图:(包含部分Okhttp实现)
二:Okhttp的项目介绍与实现网络请求的原理
官方简介:OkHttp是一个默认高效的HTTP客户端
1、HTTP2支持允许对同一主机的所有请求共享一个套接字。
2、连接池减少了请求延迟。
3、透明GZIP缩小了下载大小。
4、响应缓存完全避免了网络重复请求。
5、请求失败自动重试主机的其他IP,自动重定向:当网络出现问题时,OkHttp的不断重试,它会默默地从常见的连接问题中恢复,如果您的服务有多个IP地址,如果第一次连接失败,OkHttp将尝试备用地址,这对于IPv4+IPv6和冗余数据中心中托管的服务是必要的,OkHttp支持现代TLS功能(TLS1.3、ALPN、证书固定),它可以配置为回退以实现广泛的连接。
6、好用的API:使用OkHttp很容易,它的请求响应API设计有流畅的构建器和不变性,它支持同步阻塞调用和带有回调的异步调用。
下面对于okhttp实现网络请求的操作,就不用Okhttp的示例作为分析,还是接着Retrofit实现代码接着讲,但是有兴趣的读者,本人下面也放上通过Okhttp做网络请求的示例代码。
Okhttp网络请求解析:通过对上面的Retrofit的网络请求逻辑分析,最后执行到OkHttpCall类的execute()函数,通过okhttp对网络请求的实现操作,返回数据parseResponse(call.execute()),下面我们就具体看看Okhttp是如何实现对网络请求操作的,并对返回数据的相关处理。
第一步:在Okhttp内部会通过Dispatcher类(分发器)实现对同步、异步的网络请求进行存储和网络请求调配的,分发器中包含线程池的对象、请求缓存队列(双端队列)等内部变量。
RealCall的execute()函数实现:
第二步:在通过Dispatcher(分发器)调配相关网络请求后,会通过系统设置的多个责任链拦截器实现最终的网络请求动作的执行操作,各种拦截器包括:
1、RetryAndFollowUpInterceptor(负责重定向)
2、BridgeInterceptor(负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应)
3、CacheInterceptor(负责读取缓存以及更新缓存)
4、ConnectInterceptor(负责与服务器建立连接)
5、CallServerInterceptor(负责从服务器读取响应的数据)
通过依次各个拦截器的逻辑处理,最终在CallServerInterceptor拦截器中实现对网络数据的请求。
看看上面执行到RealCall的getResponseWithInterceptorChain()函数实现:
第三步:最后通过CallServerInterceptor拦截器实现网络请求,并返回请求对象Response,通过解析返回对象的数据,最终实现一次完整的网络请求行为。
Okhttp请求流程图:
三:项目中使用到的设计模式场景分析
3.1、建造者模式
3.2、代理模式(动态代理)
3.3、责任链模式
3.4、工厂方法模式
3.5、享元模式
3.6、外观模式
3.7、策略模式
建造者模式(Bulider模式):
定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式,它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
优点:
1、封装性好,构建和表示分离。
2、扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
3、客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
使用场景:
当一个类的构造函数参数个数超过四个,而这些参数有些是可选参数,考虑使用建造者模式,OkHttpClient比较复杂,太多属性,而且客户的组合需求多样化,所以OKhttp使用建造者模式,Retrofit类、Request类对象的创建也是通过建造者模式创建。
责任链模式:
定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
优点:
1、降低了对象之间的耦合度:该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
2、增强了系统的可扩展性:可以根据需要增加新的请求处理类,满足开闭原则。
3、增强了给对象指派职责的灵活性:当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
4、责任链简化了对象之间的连接:每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的if或者if else语句。
5、责任分担:每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
使用场景:在okhttp中的拦截器模块,执行过程用到,OkHttp3的拦截器链中,内置了5个默认的拦截器,分别用于重试、请求对象转换、缓存、链接、网络读写。
OkHttp3责任链模式图解:
代理模式(动态代理):
定义:代理模式又叫委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介,代理模式可以提供非常好的访问控制,应用比较广泛。
优点:
1、职责清晰:具体角色是实现具体的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,代码清晰,在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
2、高扩展性:具体主题角色随时会发生变化,但是只要实现了接口,接口不变,代理类就可以不做任何修改继续使用,符合“开闭原则”,另外,代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,同样符合开闭原则。
代理模式有多种不同的实现方式,如果按照代理创建的时期来进行分类:静态代理、动态代理。
1、静态代理:由程序员创建或特定工具自动生成源代码,再对其进行编译。在程序运行之前,代理类.class文件就已经被创建,代理类和委托类的关系在运行前就确定。
2、动态代理:动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件,代理类和委托类的关系是在程序运行时确定。
使用场景:在Retrofit的create函数执行时,通过动态代理模式,实现了网络请求时,通过代理类去做具体的操作实现(Method.invoke),最后实际是通过Okhttp实现网络请求的。
工厂方法模式:
定义:这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
优点:
1、用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
2、灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
3、典型的解耦框架,高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
1、类的个数容易过多,增加复杂度。
2、增加了系统的抽象性和理解难度。
3、抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
使用场景:Call接口提供了内部接口Factory,用于将对象的创建延迟到该工厂类的子类中进行,从而实现动态的配置,工厂方法模式。
策略模式:
定义:一个类的行为或其算法可以在运行时更改,这种类型的设计模式属于行为型模式,策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的context对象,策略对象改变context对象的执行算法。
优点:
1、算法可以自由切换。
2、避免使用多重条件判断。
3、扩展性良好。
缺点:
1、策略类会增多。
2、所有策略类都需要对外暴露。
使用场景:
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
CacheInterceptor实现了数据的选择策略,来自网络还是来自本地,这个场景也是比较契合策略模式场景,CacheInterceptor需要一个策略提供者提供它一个策略(锦囊),CacheInterceptor根据这个策略去选择走网络数据还是本地缓存。
缓存的策略过程:
1、请求头包含"If-Modified-Since"或"If-None-Match"暂时不走缓存。
2、客户端通过cacheControl指定了无缓存,不走缓存。
3、客户端通过cacheControl指定了缓存,则看缓存过期时间,符合要求走缓存。
4、如果走了网络请求,响应状态码为304,只有客户端请求头包含"If-Modified-Since"或"If-None-Match",服务器数据没变化的话会返回304状态码,不会返回响应内容,表示客户端继续用缓存。
享元模式:
定义:尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象,主要用于减少创建对象的数量,以减少内存占用和提高性能。
优点:
1、大大减少对象的创建,降低系统的内存,使效率提高。
缺点:
1、提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
使用场景:
1、系统有大量相似对象。
2、需要缓冲池的场景。
3、通过Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>()存放网络请求的相关对象。
4、在Dispatcher的线程池中,所用到了享元模式,一个不限容量的线程池,线程空闲时存活时间为60秒,线程池实现了对象复用,降低线程创建开销,从设计模式上来讲,使用了享元模式。
外观模式(Facade):
定义:外观模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式,该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。
优点:(迪米特法则的典型应用)
1、降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
2、对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
3、降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
使用场景:OKHttpClient里面组合了很多的类对象。其实是将OKHttp的很多功能模块,全部包装进这个类中,让这个类单独提供对外的API,这种设计叫做外观模式。
四:项目中的核心类分析
4.1、HttpServiceMethod详解
4.2、Dispatcher详解
4.3、Interceptor详解
4.4、其余次要类的简述
4.1、ServiceMethod与HttpServiceMethod详解
解析:将接口方法的调用调整为HTTP调用。在使用Retrofit时,需要自定义网络请求接口类,并通过Create函数创建相关的网络对象,这里主要就使用到
HttpServiceMethod类了,我们可以把自定义的网络请求接口的每个函数转换为一个key对象Method,value就是一个个HttpServiceMethod对象,Retrofit通过ConcurrentHashMap数据接口存放转换后的键值对,后续的网络请求调用时,实际就是通过调用HttpServiceMethod实现的。
4.2、Dispatcher解析
官方注释:关于何时执行异步请求的策略,每个调度程序都使用ExecutorService在内部运行调用,如果您提供自己的执行程序,它应该能够同时运行配置的最大调用数。
同步请求:
异步请求:
当正在执行的任务未超过最大限制64,同时runningCallsForHost(call) <maxRequestsPerHost 同一Host的请求不超过5个,则会添加到正在执行队列,同时提交给线程池。否则先加入等待队列,加入线程池直接执行没啥好说的,但是如果加入等待队列后,就需要等待有空闲名额才开始执行,因此每次执行完一个请求后,都会调用分发器的Finished方法。
分发器的线程池:
SynchronousQueue使用此队列意味着希望获得最大并发量,因为无论如何,向线程池提交任务,往队列提交任务都会失败,而失败后如果没有空闲的非核心线程,就会检查如果当前线程池中的线程数未达到最大线程,则会新建线程执行新提交的任务,完全没有任何等待,唯一制约它的就是最大线程数的个数。因此一般配合 Integer.MAX_VALUE就实现了真正的无等待。
Dispatcher是一个任务调度器,它内部维护了三个双端队列:
readyAsyncCalls:准备运行的异步请求
runningAsyncCalls:正在运行的异步请求
runningSyncCalls:正在运行的同步请求
同步请求的话,就直接把请求添加到正在运行的同步请求队列runningSyncCalls中,异步请求会做个判断,如果正在运行的异步请求不超过64,而且同一个host下的异步请求不得超过5个则将请求添加到正在运行的同步请求队列runningAsyncCalls中,并开始执行请求,否则就添加到readyAsyncCalls继续等待。
4.3、Interceptor解析
官方解析:观察、修改并可能使发出的请求和返回的相应响应短路,通常,拦截器会在请求或响应上添加、删除或转换标头。
重试及重定向拦截器:
解析:本拦截器是整个责任链中的第一个,这意味着它会是首次接触到Request与最后接收到Response的角色,在这个拦截器中主要功能就是判断是否需要重试与重定向,重试的前提是出现了RouteException或者IOException,一但在后续的拦截器执行过程中出现这两个异常,就会通过recover方法进行判断是否进行连接重试,重定向发生在重试的判定之后,如果不满足重试的条件,还需要进一步调用followUpRequest根据Response的响应码(当然,如果直接请求失败,Response都不存在就会抛出异常),followup最大发生20次。
桥接拦截器:
解析:连接应用程序和服务器的桥梁,我们发出的请求将会经过它的处理才能发给服务器,比如设置请求内容长度,编码,gzip压缩,cookie等,获取响应后保存Cookie等操作。
缓存拦截器:
解析:在发出请求前,判断是否命中缓存,如果命中则可以不请求,直接使用缓存的响应。
1、如果从缓存获取的Response是null,那就需要使用网络请求获取响应。
2、如果是Https请求,但是又丢失了握手信息,那也不能使用缓存,需要进行网络请求。
3、如果判断响应码不能缓存且响应头有no-store标识,那就需要进行网络请求。
4、如果请求头有no-cache标识或者有If-Modified-Since/If-None-Match ,那么需要进行网络请求。
5、如果响应头没有no-cache标识,且缓存时间没有超过极限时间,那么可以使用缓存,不需要进行网络请求。
6、如果缓存过期了,判断响应头是否设置Etag/Last-Modified/Date,没有那就直接使用网络请求,否则需要考虑服务器返回304,并且,只要需要进行网络请求,请求头中就不能包含only-if-cached,否则框架直接返回504。
连接拦截器:
解析:这个拦截器中的所有实现都是为了获得一份与目标服务器的连接,在这个连接上进行HTTP数据的收发。
请求服务器拦截器:
解析:真正发起HTTP请求,并拿回响应报文。
网络请求与拦截器图解:
拦截器总结:
1、整个OkHttp功能的实现就在这五个默认的拦截器中,所以先理解拦截器模式的工作机制是先决条件,这五个拦截器分别为: 重试拦截器、桥接拦截器、缓存拦截器、连接拦截器、请求服务拦截器,
每一个拦截器负责的工作不一样,就好像工厂流水线,最终经过这五道工序,就成了最终的产品,但是与流水线不同的是,OkHttp中的拦截器每次发起请求都会在交给下一个拦截器之前干一些事情,
在获得了结果之后又干一些事情。整个过程在请求向是顺序的,而响应则是逆序。
2、当用户发起一个请求后,会由任务分发器Dispatcher将请求包装并交给重试拦截器处理。
3、重试拦截器在交出(交给下一个拦截器)之前,负责判断用户是否取消了请求,在获得了结果之后,会根据响应码判断是否需要重定向,如果满足条件那么就会重启执行所有拦截器。
4、桥接拦截器在交出之前,负责将HTTP协议必备的请求头加入其中(如:Host)并添加一些默认的行为(如:GZIP压缩),在获得了结果后,调用保存cookie接口并解析GZIP数据。
5、缓存拦截器顾名思义,交出之前读取并判断是否使用缓存,获得结果后判断是否缓存。
6、连接拦截器在交出之前,负责找到或者新建一个连接,并获得对应的socket流,在获得结果后不进行额外的处 理。
7、请求服务器拦截器进行真正的与服务器的通信,向服务器发送数据,解析读取的响应数据,在经过了这一系列的流程后,就完成了一次HTTP请求。
4.4、其余次要类的简述
OkHttpClient:通信的客户端,用来统一管理发起请求与解析响应。
Call:Call是一个接口,它是HTTP请求的抽象描述,具体实现类是RealCall,它由CallFactory创建。
Request:请求,封装请求的具体信息,例如:url、header等。
RequestBody:请求体,用来提交流、表单等请求信息。
Response:HTTP请求的响应,获取响应信息,例如:响应header等。
ResponseBody:HTTP请求的响应体,被读取一次以后就会关闭,所以我们重复调用responseBody.string()获取请求结果是会报错的。
Interceptor:Interceptor是请求拦截器,负责拦截并处理请求,它将网络请求、缓存、透明压缩等功能都统一起来,每个功能都是一个Interceptor,所有的Interceptor最终连接成一个Interceptor.Chain,典型的责任链模式实现。
StreamAllocation:用来控制Connections与Streas的资源分配与释放。
RouteSelector:选择路线与自动重连。
RouteDatabase:记录连接失败的Route黑名单。
五:常见问题汇总
5.1、应用拦截器和网络拦截器的区别?
解析:
应用拦截器(Interceptor)
1、不需要担心中间过程的响应,如重定向和重试。
2、总是只调用一次,即使HTTP响应是从缓存中获取。
3、观察应用程序的初衷,不关心OkHttp注入的头信息如: If-None-Match。
4、允许短路而不调用Chain.proceed(),即中止调用。
5、允许重试,使Chain.proceed()调用多次。
6、Okhttp的调用的顺序不一样。
网络拦截器(Network interceptor)
1、能够操作中间过程的响应,如重定向和重试。
2、当网络短路而返回缓存响应时不被调用。
3、只观察在网络上传输的数据。
4、携带请求来访问连接。
5.2、Android中网络请求框架的演进过程?
5.3、OkHttp怎么实现连接池
为什么需要连接池:
频繁的进行建立Sokcet连接和断开Socket是非常消耗网络资源和浪费时间的,所以HTTP中的keepalive连接对于降低延迟和提升速度有非常重要的作用,keepalive机制是什么呢,也就是可以在一次TCP连接中可以持续发送多份数据而不会断开连接,所以连接的多次使用,也就是复用就变得格外重要了,而复用连接就需要对连接进行管理,于是就有了连接池的概念。
OkHttp中使用ConnectionPool实现连接池,默认支持5个并发KeepAlive,默认链路生命为5分钟。
怎么实现连接池:
1、首先,ConnectionPool中维护了一个双端队列Deque,也就是两端都可以进出的队列,用来存储连接。
2、然后在ConnectInterceptor,也就是负责建立连接的拦截器中,首先会找可用连接,也就是从连接池中去获取连接,具体的就是会调用到ConnectionPool的get方法,也就是遍历了双端队列,如果连接有效,就会调用acquire方法计数并返回这个连接。
3、如果没找到可用连接,就会创建新连接,并会把这个建立的连接加入到双端队列中,同时开始运行线程池中的线程,其实就是调用了ConnectionPool的put方法。
4、其实这个线程池中只有一个线程,是用来清理连接的,也就是上述的cleanupRunnable。
5、怎样属于空闲连接,一个方法acquire计数方法,在RealConnection中,有一个StreamAllocation虚引用列表allocations,每创建一个连接,就会把连接对应的StreamAllocationReference添加进该列表中,如果连接关闭以后就将该对象移除。
6、总结,主要就是管理双端队列Deque<RealConnection>,可以用的连接就直接用,然后定期清理连接,同时通过对StreamAllocation的引用计数实现自动回收。
5.4、如何支持自签名网站https的访问?
解析:通过鸿洋的开源项目okhttputils的工具类HttpsUtils,实现对SSLSocketFactory和X509TrustManager变量的设置。
请求对象的设置代码:
5.5、如何通过一张图来整体汇总网络通信的过程及重要知识点?
六:扩展阅读
1、https://blog.csdn.net/u012346890/article/details/111568776(Okhttp相关知识点总结)
3、https://blog.csdn.net/weixin_43662090/article/details/114868599(okhttp学习系列之interceptor和network interceptor的区别)
4、https://blog.csdn.net/u012165769/article/details/109212157(Android 网络框架之Retrofit源码解析)
6、https://www.jianshu.com/p/030222c4df4e(Proxy.newProxyInstance理解)
8、https://github.com/hongyangAndroid/okhttputils(okhttp的常见封装实现)