Retrofit原理解析

       在前面我们分析了okhttp的原理,但okhttp本质上是一个HTTP层面的框架。它的核心关注点在于HTTP协议的实现,包括封装请求报文Request,将Request通过TCP连接传输到服务器并接收服务器返回的响应报文Response。一切都是围绕HTTP协议相关的东西展开,在实际开发中,直接使用okhttp可以实现网络请求,但每次都要处理HTTP相关的东西。这就和我们平常的开发模式不太一样了。我们更希望能够遵循面向对象编程,创建对象,传入参数然后调用相应的函数就可以实现网络请求,至于具体的HTTP层面的细节我们不想关心。所以就需要对okhttp进行再一次的封装,或者直接使用Retrofit。

       Retrofit,个人理解为可以是一个API层面的框架。从代码设计的角度,比HTTP层面更高级。通过Retrofit实现网络请求,我们只需要声明一个接口,来代表访问的服务模块,接口里面的函数代表某个具体的服务接口。Retrofit提供了一系列的注解,可以添加到函数和请求参数上,来提供HTTP请求的关键信息。注解大家都很熟悉,是Java平台的元数据,用来修饰类,字段,方法等这些程序元素的。主要用于框架实现中,既可以作为标志性注解,也可以提供附加信息。这里引入注解,就相当于将Java接口变成了一个配置文件。通过注解提供的关键信息,Retrofit会进行配置的解析,并完成HTTP层面的工作。函数里的请求参数就是发送给服务器的数据,函数的返回值就是服务器给我们返回的数据。当我们想要发起网络请求时,就可以向普通的面向编程一样,拿到对象,传入参数,然后调用相应的函数,就可以不用管HTTP层面的细节了。

一:接口定义

      这里以一个简单的例子,来一步步的分析Retrofit的工作原理。这个例子和官网很类似:

ServiceInterface

       这里简单的声明了一个Java接口,来代表向服务器发送请求用户列表的请求。在开发者眼里,我们不需要关注HTTP层面的细节,只需要知道当listRepos1()函数被调用的时候,就代表着向服务器发送请求了。请求参数RequestBody是客户端发送给服务器的,返回值Call<ResponseBody>是服务器返回给我们的。

       首先需要考虑的点是Call,这个是Retrofit里面定义的Call:

Retrofit$Call

      在okhttp中,也有一个Call类。okhttp中的Call和Retrofit中的Call功能和里面的函数都几乎一样。不一样的地方在于设计的层面,okhttp中的Call是对HTTP请求的封装,所以它关注的都是HTTP层面的东西,包括请求报文对应的Request,响应报文对应的Response。它要做的就是把Reqeust传递给服务器,并接收服务器返回的Response。在Call眼里,它关注的都是HTTP的报文,不会关注具体的业务类型。而Retrofit里面的Call就不一样,它首先是个范型类,类型参数T代表的是业务类型。也就是说,服务器返回的响应报文中的body数据,Retrofit会自动给我们转换为对应的业务类型对象。但在okhttp中就不关注这个,响应报文中的body就是ResponseBody,代表原始的响应报文的数据,至于这个body如何解析,对应哪个业务类型,是开发者自己的事情。okhttp不关注这些,只关注于报文的传输。

       Call中另一个不一样的地方在于Response不一样。okhttp中的Resonse对应的就是原始的响应报文,包括报文中的状态码,头部字段集合和ResponseBody,都是一些原始的信息,都是原始的HTTP报文,和业务类型一点关系都没有。但到了Retrofit中,它的Response也是一个范型类:

Retrofit$Resonse

       Retrofit中的Response可以理解为是对okhttp中的Response的封装。它有三个字段:rawResponse,body和errorBody。其中rawResponse对应的就是okhttp中的Response,body对应的是HTTP请求成功之后返回的数据,经过了ResponseBody到具体的业务类型的转化,errorBody是请求失败之后返回的原始ResponseBody。一个Response可能成功,可能失败。成功了只会有body和rawResponse,失败了就会有rawResponse和errorBody。会通过Response的error()和success()来生成对应的Response对象。

        Call和Response都是okhttp和Retrofit两个框架中都存在的类,对于这两个同名类的对比,可以更清楚的理解两个框架在设计角度的不同。

        那回到定义的接口中,现在我们就可以认识到函数的请求参数对应的就是请求报文里面的body,Retrofit中会有一个从业务类型对象到ReqeustBody的转换过程。函数的返回值对应的是响应报文的body,同样Retrofit中也会有从ResponseBody到业务类型对象的转换过程。这两个转换过程后面会分析到。

二:CallAdapter

       那现在有了接口,下一步就需要创建接口的对象。正常编程是创建接口的实现类,然后创建实现类的对象,在调用对应的函数。但其实这些接口函数的重点在于函数和请求参数上的注解的处理,得到HTTP请求的关键信息,这些函数自己没有什么特殊的逻辑。所以这些函数通用的逻辑就是处理HTTP层面的逻辑,不一样的地方在于注解提供的信息会不同。接口的实现类和对象是通过Retrofit的create函数来实现,内部使用了动态代理:

Retrofit$create()

       create()通过动态代理的方式给指定的接口生成了一个实现类,并创建了对象。当其中某个函数被调用的时候,就会来到指定的InvocationHandler里面的invoke里面来。然后在invoke()函数里实现了函数的解析和其他的HTTP层面的工作。

        在okhttp中,会通过Request来构造一个Call,通过这个Call来开启同步或者异步的HTTP请求。而到了Retrofit中,这部分工作交给了OkhttpCall来完成。OkHttpCall是Retrofit内置的类,也可以理解为异步任务,代表要执行的网络请求,它内部完成了HTTP层面逻辑的处理。但在使用Retrofit中,我们不会直接使用OkHttpCall这个类,因为这个类是Retrofit的内部细节,依赖于okhttp,我们不需要关注这些。我们直接使用的是通过CallAdapter转换后得到的另外一个类。

CallAdapter

      从源码中看,CallAdapter的作用是把一个Call<R>的对象转化为另一个T对象。其中的Call<R>指的就是OkhttpCall,T对象是一个作用和Call类似的对象,也是代表的异步任务。CallAdapter的设计给Retrofit带来了很大的扩展,让Retrofit可以和很多库配合使用,从而将OkhttpCall转化为其他的我们熟悉的并且可以代表任务的类。比如可以兼容RxJava,从而把OkhttpCall转化为Observable使用。

       CallAdapter对象通过对应的Factory来生成,而Factory被配置到了Retrofit中。对于一个原生的Retrofit,在没有配置其他的Factory的情况下,Retrofit内部使用的是定义在PlatForm里面的defaultCallAdapterFactory:

Retrofit

     在Android平台下,Platform对应的是它的子类Android:

Android

      而Android里面使用的Factory是ExecutorCallAdapterFactory:

ExecutorCallAdapterFactory

     其中get()函数里面的returnType参数代表的是被调用接口的返回类型,annotations是声明在函数上的注解。那get()函数里面的逻辑就表明,默认的Retrofit只支持Call类型,也就是说声明的函数返回值类型必须是Call。然后Retrofit会把OkhttpCall转换为ExecutorCallbackCall,那这个ExecutorCallbackCall和原来的OkhttpCall都是Call的实现类,它们又有什么区别呢:

ExecutorCallbackCall

       通过代码发现,ExecutorCallbackCall可以简单理解为OkhttpCall的代理,几乎所有的逻辑全都交给OkhttpCall来做。唯一不一样的地方在于当调用enqueue来执行异步请求的时候,会通过callbackExecutor来执行Callback的回调,而这个callbackExecutor是一个Executor,它的逻辑也很简单:

MainThreadExecutor

        MainThreadExecutor内部通过Handler,从而将Callback的调用切回主线程中来。由此我们可以知道,默认情况下,Retrofit只支持接口函数的返回类型为Call,而Retrofit帮我们做的也只是线程的切换。

       在实际开发中,更多的是将Retrofit和RxJava配合使用,这个时候就需要给Retrofit添加RxJava2CallAdapterFactory,和RxJava2来配合使用。在RxJava2中,除了Observable之外,还有许多其他类似的类,像Flowable,Single,Completable等等,这里我们只以Observable为例分析就好。当Retrofit配置了RxJava2CallAdapterFactory之后,就可以把接口返回的返回类型声明为Observable,而Observable中的类型参数可以分为三种情况,每种情况的处理逻辑也不一样:

       业务类型:就是直接指定ResponseBody对应的业务类型,Retrofit会做转换,将对应的业

                         务类型对象返回。如果返回的状态码不是200,那么就会封装一个                                                HttpException。其他的网络错误会被封装为IOException,这两种情况都会回                            调Observer的onError函数。

       Response<T>:这里的Response是Retrofit中的Response,在这种情况下,所有的网络                                     请求结果,无论状态码成功还是失败,都会回调onNext。其他的网络错                                     误会回调onError。

        Result:这是RxJava2CallAdapterFactory提供的一个类,也可以用作接口函数的返回                              值。当声明为这个类型的时候,那么所有的情况都会回调onNext函数,用户通                          过接收的Result对象来判断成功还是失败。Result内部封装了Response和                                  Throwable。

       在了解了如何使用RxJava2CallAdapterFactory之后,我们继续看它的原理,直接看它的get()函数:

get()-1

      首先是对函数的返回类型做判断,判断是否是RxJava中的类型。

get()-2

       判断是Rxjava中的类型了之后,还要判断returnType是不是参数化类型ParameterizedType。是的话,会获取到类型实参,保存到observableType。而这个observableType也可能是参数化类型,所以它的原始类型被保存到了rawObservableType中。

       根据前面的分析,我们可以知道observableType可能会有三种情况:Response,Result和具体的业务类型。如果是Response或者Result的话,就会继续获得它的类型实参,然后赋值给responseType。responseType代表的是ResponseBody映射后的业务类型,如果不是Response或者Result的话,那么responseType就直接等于observableType。在完成isResult和isBody的赋值之后,调用RxJava2CallAdapter的构造函数,生成对象。继续看RxJava2CallAdapter的adapt()函数:

RxJava2CallAdapter$adapt

      adapt()函数里面会分为三种情况Response,Result和具体的业务类型,其中Response是核心,我们先来看Response。在Observable声明的类型实参是Response的前提下,那么adapt()返回的Observable就是刚开始构造的responseObservable。构造对象的时候会考虑isAsync字段,代表是不是异步。区别也很简单,就是调用Call的execute还是enqueue。我们直接看同步的就好,因为实际开发中,我们都是通过Scheduler来控制线程。

CallExecuteObservable

       CallExecuteObservable的核心逻辑都在subscribeActual函数里,其中的代码也比较简单,一看就懂。其中的originalCall就是OkhttpCall,subscribeActual一方面通过调用OkhttpCall的execute来发送HTTP请求,另一方面完成了RxJava的语义,包括onNext,onError的调用。

       Response对应的Observable工作过程分析完了之后,接下来看Result,它对应的是ResultObservable。

ResultObservable

       ResultObservable的逻辑也很简单,当有Observer订阅自己的时候,就会通过ResultObserver来封装这个Observer,然后再使用封装之后的ResultObserver来订阅upstream,而upstream就是前面的CallExecuteObservable。而对于Result前面已经提到,不管什么情况,都会只有onNext调用,相应的Response和Throwable会被封装到Result里面,这些是通过Result的response和error()函数来实现。

      最后一种情况就是具体的业务类型,它对应的是BodyObservable:

BodyObservable

       BodyObservable的逻辑很类似,也是通过BodyObserver来封装订阅的Observer。不一样的地方在于会对Response来判断,区分成功的状态码和失败的情况。成功的时候直接通过body()返回业务类型的对象,失败的时候会封装成HttpException,来调用onError。

      至此,RxJava2CallAdapterFactory允许的三种类型和工作原理都分析完了。其中Response部分是核心,另外的两个都是在Response的基础上进行了后续处理和封装。在实际开发中,通过给Retrofit配置RxJava2CallAdapterFactory,就可以指定函数的返回类型为Observable。然后就可以通过RxJava的开发方式来操作网络请求。

三:ServiceMethod

       分析完了CallAdapter的原理和作用之后,继续回到Retrofit的create()函数中:

ServiceMethod

      当接口的函数被调用的时候,Retrofit会给这个函数生成一个对应的ServiceMethod对象,并且会缓存。一旦创建成功了之后,以后会直接复用。而ServiceMethod主要完成四个工作:方法配置的解析,主要包括方法的注解,请求参数的注解和数据类型;Call的adapt;生成这次HTTP请求需要的Request和Okhttp中的Call;ResponseBody的Converter工作。

       了解了ServiceMethod的职责,接下来一点点的分析。ServiceMethod相当于一个功能复杂的产品,所以它的构建工作由对应的Builder来负责。首先,构造对应的Builder对象:

Builder

       Builder的构造函数也比较简单,就是利用反射,拿到Method的注解,请求参数的类型和请求参数的注解。其中请求参数的注解是个二维数组,一个函数可以有多个请求参数,一个请求参数可以有多个注解。接下来,就会调用build()来构造一个ServiceMethod对象。build()函数里的逻辑就比较复杂了,我们一点点的看。

build()-1

     首先是生成所需要的CallAdapter对象,通过createCallAdapter()来实现:

createCallAdapter

       而createCallAdapter()由调用了Retrofit的callAdapter()函数,而callAdapter()又继续调用了nextCallAdapter,所以直接看nextCallAdapter里面的逻辑:

nextCallAdapter()

        而nextCallAdapter里面的逻辑就很简单明了了,对内部的callAdapterFactories进行遍历,它是一个List,直到能找到合适的CallAdapter.Factory为止。而callAdapterFactories,可以通过Retrofit.Builder的addCallAdapterFactory来添加。需要注意的一点是,这个List是有顺序的,自定义添加的在前面,系统内置的在后面。因为build()的时候就是按照这个顺序添加的:

addCallAdapterFactory

      而不管是Retrofit内置的Factory,还是自己添加的RxJava的Factory,它们的匹配原理前面已经分析过了,就是根据函数的返回类型来判断的。如果找不到合适的CallAdapter,就会抛出异常。当确认了CallAdapter之后,responseType变量的值也就确定了,它代表的就是ResponseBody对应的业务类型。

      确定了CallAdapter之后,下一步会确认ResponseBody的Converter,也就是将ResponseBody转化为具体业务类型的Converter。这部分逻辑在createResponseConverter()中:

createResponseConverter

      createResponseConverter又调用了Retrofit的responseBodyConverter函数,而它又调用了nextResponseBodyConverter,所以直接看nextResponseBodyConverter():

nextResponseBodyConverter

        而nextResponseBodyConverter里面的逻辑也很简单,就是对Retrofit的converterFactories遍历,一个一个找,找不到就会抛出异常。那现在就涉及到了Retrofit另一个核心类Converter。

Converter

        Converter翻译过来是转换器的意思,从设计的角度来讲,它是一个通用的接口,就是把一个F类型的对象转化为一个T类型的对象。在Retrofit中,它的作用有两个方面:将请求参数转化为RequestBody;将ResponseBody转化为函数返回值对应的类型。Converter的对象也是通过对应的Factory来得到,Factory中有相应的requestBodyConverter和responseBodyConverter函数。那现在我们需要的是一个对ResponseBody进行转化的Converter,所以在nextResponseBodyConverter中它会依次调用每个Factory的responseBodyConverter函数。在没有额外配置的情况下,Retrofit内置的Factory是BuiltInConverters:

BuiltInConverters

       BuiltInConverters里面的responseBodyConverter逻辑也很简单,通过代码来看,它只支持ResponseBody和void。也就是说,在没有给Retrofit额外配置的前提下,接口函数的返回类型只能是Call<ResponseBody>,拿到的是原始的响应报文的body数据,没做任何的类型转换。相当于从一个ResponseBody对象转化到了另一个ResponseBody对象,在这个转化过程中,Retrofit做了什么呢,这里以BufferingResponseBodyConverter为例:

BufferingResponseBodyConverter

      BufferingResponseBodyConverter里面先调用了Utils的buffer函数:

Utils$buffer

      buffer函数里面的逻辑也很简单,就是把原来的ResponseBody里面的数据读取出来,保存到了一个Buffer对象中。并以这个Buffer对象生成了一个新的ResponseBody对象,content-type和content-length和以前一样。BufferingResponseBodyConverter最后会调用原来的ResponseBody的close函数,来释放资源。前面学习okhttp的时候提到了,返回的ResponseBody使用了一定要关闭。

        所以通过BufferingResponseBodyConverter我们可以直到,Retrofit帮我们做的就是把响应报文里的数据读取到内存里,后续可以直接使用,并且还帮我们把ResponseBody关闭了。

        但ResponseBody毕竟代表的是原始响应报文的body,这不是我们想要的,我们想要的是它转换之后的业务类型对象。对于这个转换操作,Retrofit原生是做不到的,只能我们自己添加新的Converter。在实际开发中,使用的是GsonConverterFactory,实现对象和json字符串间的解析。接下来看一下GsonConverterFactory的实现原理:

GsonConverterFactory

       在GsonConverterFactory眼中,它可以支持任意类型。可以把ResponseBody转化为任意的类型,所以它没有对type进行判断,直接返回了一个GsonResponseBodyConverter,继续看它的converter函数:

converter

      这里面就是Gson的基本使用了,将ResponseBody当作Json字符串来处理,转化为对应的Java对象。转换完成后,会把ResponseBody关闭掉。如果转换过程出现了错误,就会抛出异常。

       当给Retrofit配置了自定义的Converter之后,和前面的CallAdapter一样,它是有顺序的。只不过这次BuiltInConverters在钱,自定义的Converter在后面:

       再加上GsonConverterFactory支持所有的类型,所以如果需要添加多个Converter的时候,最好把Gson的放在最后面,作为一个兜底的方案。

       回到ServiceMethod.Builder的build函数中,到现在已经确认了所需要的CallAdapter,和对ResponseBody进行转换的Converter。接下来就需要对方法的注解和请求参数的注解来进行解析。首先解析的是方法的注解,通过parseMethodAnnotation来实现:

parseMethodAnnotation

     方法上的注解主要有两方面,一方面是确认请求方式和Url,每一个标准的HTTP请求在Retrofit里都有一个对应的注解,比如@Post,@Get,@Head等等。这些类型的注解通过parseHttpMethodAndPath来解析,目的是确认请求方式和Url:

parseHttpMethodAndPath

       另一类型的注解,源码中叫Encode Annotation。其实也就是指定请求报文中body数据类型的注解,有两个:Multipart和FormUrlEncoded,这两个注解大家一看名字就能明白含义。

        完成了方法上的注解之后,下一步就是对请求参数的数据类型和注解的解析。这部分逻辑通过parseParameterAnnotation()来实现,返回一个ParameterHandler。Retrofit声明了很多个HTTP相关的注解,其中一部分是用来修饰请求参数的。这些用于请求参数的注解的作用可以分为两类,一类是对Url,Head,Path或者Query的补充,而另一类就是指定当通过POST请求向服务器传递数据的时候,body的构造过程的。每个参数的解析都会通过一个ParameterHandler对象来实现,这是一个抽象类。刚才提到的,每一个可以出现在请求参数上的注解,都对应ParameterHandler的一个子类:

ParameterHandler

      这些子类,我们只关注Field和Body。其他的都很类似,有兴趣的朋友看源码就明白了。ParameterHandler是个抽象类,它里面只有一个apply函数:

ParameterHandler

       其中T代表的就是被修饰的请求参数,而这个RequestBuilder是Retrofit定义的类,用来构建代表请求报文,并且是定义在okhttp中的Request对象。那自然而然的就有了一个问题,Retrofit里面的RequestBuilder和okhttp中的Request.Builder有什么不一样呢。个人认为两个Builder功能类似,只不过Retrofit里面的RequestBuilder兼容包括了很多种情况,类似于一个融合体。在okhttp中,Request.Builder包含四部分内容:请求方式,url,Headers和ReqeustBody。但是在实际开发中,其中的url和RequestBody又可以拆分成好几种情况。比如url会有baseUrl和relativeUrl,而RequestBody包括FormBody和MultiPartBody。而RequestBuilder就把这些所有的情况都融合在了一起,按需配置,所以它里面的字段更多一些,更细一些:

RequestBuilder

        Retrofit就是利用这个RequestBuilder来构建Request对象,而构建Request对象所需要的关键信息都被定义在了方法的注解,请求参数和它的注解上。其中每个请求参数的解析都会对应一个ParameterHandler对象,里面包含了解析的逻辑。每个ParameterHandler对象解析之后的结果,都是Request其中的一部分,都会被用在RequestBuilder中。所以Retrofit的处理策略是,对这些ParameterHandler对象进行循环遍历,依次调用它的apply函数。 apply函数接收到的都是同一个RequestBuilder对象,这样每个ParameterHandler就在apply中把自己得到的关键信息配置在RequestBuilder上,用于后面Request对象的创建。

       函数里的请求参数不可以没有Retrofit定义的注解,并且最多只能有一个Retrofit定义的注解。每个参数都会通过parseParameterAnnotation()来生成对应的ParameterHandler对象。这里我们就只分析其中的两个注解,一个@Body,一个是@Field。

       首先来看@Body,这个注解修饰的请求参数就代表着最终的RequestBody,相当于给了开发者完全的控制权,由自己来制定RequestBody到底什么样。在Retrofit中,会把这个参数转化为ReqeustBody对象,而这部分操作也是由Converter来完成:

@Body

       具体的Converter通过Retrofit的requestBodyConverter来得到,和前面的Response类似,最终会通过配置的Factory的requestBodyConverter()来返回。先来看一下内置的BuiltInConverters:

BuiltInConverters$requestBodyConverter

        其中的type就是方法里声明的参数的类型,通过代码可以看到,原生的Retrofit只支持ReqeustBody。也就是说,你只有直接指定了一个ReqeustBody之后,Retrofit才能正确的发起网络请求。也是相当于从一个RequestBody的对象转换到了另一个RequestBody的对象,不过这一次,在这过程中Retrofit什么也没干:

RequestBodyConverter

        直接返回了,什么处理都没有。但同样,指定RequestBody也不是我们想要的开发方式,我们不想关注HTTP层面的对象,只想和java对象打交道。就需要一个Converter来实现java对象到RequestBody的转换,常用的还是gson。直接来看GsonRequestBodyConverter的逻辑:

GsonRequestBodyConverter

        里面都是Gson的使用,简单来说就是把java对象转化为对应的json字符串,然后把这个字符串写入到Buffer中。最后以这个Buffer生成了一个RequestBody对象,需要注意的是,由于我们传递的数据格式是json,所以content-typ应该是application/json,这个在GsonRequestBodyConverter中已经定义好了:

GsonRequestBodyConverter

        有了Converter之后,我们就可以直接声明要请求参数对应的业务类型,Retrofit会帮我们完成后续的转换操作。对于@Body注解,有了合适的Converter之后,就会生成ParameterHandler.Body对象:

ParameterHandler.Body

        而它的apply函数逻辑也很简单,就是利用Converter转化为ReqeustBody,然后将这个RequestBody设置给了RequestBuilder,这样RequestBuilder后期就可以构建出正确的并且包含我们指定信息的Request了。

     另一个注解是@Field,这个大家一看名字就比较熟悉,对应的是常用的表单提交,提交的数据是key-value的形式。

@Field

        @Field注解首先要和FormUrlEncoded配合使用,否则就报错。当使用了FormUrlEncoded注解之后,ServiceMethod的isFormEncoded就会==true,这样后面在创建RequestBuilder对象的时候,就会自动使用FormBody,从而让content-type==application/x-www-form-urlencoded。

RequestBuilder

      通过Field的value函数可以得到key的名字,而value就来源于修饰的请求参数。我们最终要的是一个String,对应的请求参数可以直接是String,也可以其他的类型,但最终都要转化为String,所以这个地方需要的是一个能转换为String的Converter,最终会调用配置的Converter.Factory的stringConverter函数,但是BuiltInConverters没有重写这个函数,所以使用的是默认实现,而默认实现返回的是null。

stringConverter

       在没有配置其他的Converter前提下,Retrofit最终会使用内置的ToStringConverter,它的逻辑就是简单的调用toString函数:

ToStringConverter

       Converter没问题了之后,@Field修饰的参数的解析逻辑会被封装到ParameterHandler.Field里面:

ParameterHandler.Field

       而它里面的apply就将解析道的key和value,通过ReqeustBuilder的addFormField来添加到Request,

addFormField

        而RequestBuilder又会调用FormBody.Builder的函数,这就是我们比较熟悉的okhttp的逻辑了。总之,Retrofit对于请求参数的解析被包含在了对应的ParameterHandler。后面需要创建Request的时候,就会依次调用这些ParameterHandler的apply函数,然后每个ParameterHandler把解析到的关键信息添加到RequestBuilder中,从而构造正确的Request对象。

       build()函数在完成对于方法的解析工作之后,最后会做一些HTTP层面的校验,避免不符合HTTP规范的使用,然后就会创建ServiceMethod对象:

ServiceMethod.Builder$build()

三:OkhttpCall

       当有了ServiceMethod对象之后,基本上就有了创建Request对象所需要的一切,接下来就会创建OkhttpCall对象。Retrofit执行网络请求内部是依赖okhttp的来进行HTTP层面的逻辑的,而且这种绑定算是强依赖。Retrofit设计者应该是也没打算依赖其他的HTTP框架来实现HTTP请求。而使用okhttp框架,进行网络请求的逻辑就被放在了OkhttpCall中。根据前面得到的ServiceMethod对象和传入来的请求参数,构造了okhttpCall对象。然后调用ServiceMethod的adapt将它转换为其他的类型,这部分逻辑我们已经分析过来,最终可能是Call,也可能是Observable,或者其他自定义的类型。

       这里我们就以Call为例来分析,前面提到,adapt后的Call类型为ExecutorCallbackCall。它只是OkhttpCall的一个代理,几乎所有的操作还是交给OkhttpCall来做。这里我们只分析同步调用,来看一下OkhttpCall的execute函数:

execute()

      由于依赖Okhttp,所以首先就需要一个Okhttp里面的Call对象,通过createRawCall()来创建:

createRawCall()

      而createRawCall又调用了ServiceMethod的toCall函数,并传入了具体的请求参数:

toCall

        toCall()中首先创建了一个RequestBuilder,并考虑从方法和请求参数的注解上获得的关键信息。接下来会判断传进来的请求参数的个数是否正确,然后就开始对ParameterHandler的数组进行遍历,依次调用其中的每个ParameterHandler的applay函数。最后通过RequestBuilder的build构建一个代表请求报文的Request对象,然后再利用Call.Factory的newCall来生成一个okhttp的Call,而这个callFactory一般就是配置的OkhttpClient。

        那现在有了okhttp需要的Call,回到execute()函数中:

execute()

       接下俩会判断是否cancle,如果取消了,会调用rawCall的cancel函数。然后就是走okhttp的逻辑,调用okhttp的call的execute函数,来执行网络请求,具体的过程我们已经在okhttp那一章分析过了。

       当execute执行完毕了之后,就意味着网络请求结束了,但是execute返回的是代表原始响应报文的Response,还需要进一步通过parseResponse来解析:

parseResponse

        原始的rawResponse除了body需要我们解析之外,其他的不需要处理。所以先把body拿出来了,并重新build了一个rawResponse,将其中的body换成一个空的。execute最终会返回一个定义在Retrofit中的Response对象。如果返回的状态表示失败,那么就调用Response的error函数;如果代表成功,在继续考虑有没有body。如果没有body,直接调用Response的success,然后传递一个null就好了。如果有body,就会通过sercieMethod的toResponse来实现ResponseBody到T的转换,而ServiceMethod内部也只是简单的调用了Converter的convert函数:

toResponse

       convert之后,Response里面的body就是T类型的对象了。在没有配置其他的CallAdapter的前提下,无论是同步也好还是异步也好,得到的都是一个Response对象,开发者在继续调用body()来得到对应的java对象。如果配置了RxJava2CallAdapter的话,就可以直接在next中得到对应的java对象,RxJava2CallAdapter内部会自动的脱去Response的外衣,这个前面也分析过了。以上这些是针对Call同步调用流程的分析,异步的流程也很类似,就不再赘述了。

        至此,Retrofit的原理我们就分析完了。相比较okhttp而言,Retrofit会更简单一些。因为Retrofit严重依赖okhttp,通过okhttp完成了HTTP层面的核心逻辑。Retrofit更多的是改变我们的编程方式,让我们执行网络请求更容易,更符合面向对象编程的范式。可以声明一个Java接口作为配置文件,通过注解添加HTTP的关键信息。也不需要关注HTTP层面的逻辑,直接声明具体的业务类型对象就好,Retrofit会帮我们完成java对象和HTTP语义之间的转换。简而言之,让开发者更容易的通过面向对象的编程方式来执行一个网络请求,并且不用关注具体的HTTP细节。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272