引言
Retrofit 是 Github 上面 squre 组织开发的一个类型安全的 Http 客户端,它可以在 Java 和 Android 上面使用,并且对Java8做好了适配。Retrofit 将描述
请求的接口转换为对象,然后再由该对象去请求后台。在有了Android Async Http和Volley之后,rxjava+Retrofit+okhttp的网络请求框架能够流行起来呢?一个最主要的原因是它在保证效率的同时,使Http请求变得简单,而且代码的可读性更好。
1.rxjava+Retrofit+okhttp使用
以获取考拉分类内容为例,其接口定义如下:
1)请求方式
GET http://open.kaolafm.com/v1/content/list?appid={appid}&sign={sign}&openid={openid}&cid={cid}&page={page}&size={size}
2)认证参数
参数名称 类型 是否必需 描述
appid string 是 应用id
sign string 是 签名
openid string 是 设备标识
3)请求参数
参数名称 类型 是否必需 描述
cid string 是 分类id
page string 否 页数 默认为1
size string 否 每页数据条数 默认为10 最大50
返回结果示例如下:
|
|
使用rxjava+Retrofit+okhttp的话,只需要以下三步,第一,声明Client:
|
|
第二,定义号接口:
|
|
第三,调用:
|
|
显然,代码的可读性大大加强了。
2.准备知识:Java动态代理
代理模式是常用的设计模式,其特征是代理类与委托类具有相同的接口,在具体实现上,有静态代理和动态代理之分。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务,也就是说代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
如下是一个Java静态代理的模式:
显然,如果我们要在所有实现接口的对象执行完该方法之前和之后,都进行同样的操作(如打log),则每增加一个接口,就需要增加一个代理类,显然这样太麻烦。
如果使用动态代理的话,就可以不用重复定义,还是刚刚那个例子,可以这样处理:
|
|
使用时也很简单:
|
|
3.Retrofit原理
前面分析了动态代理,那么回到Http请求这件时上来,所有的Http请求都有一个共同特征:在发送请求之前需要解析参数,在发送请求之后需要处理结果(一般是将json字符串转化为java bean),结合上面的动态代理,我们很自然的想到使用Java动态代理来处理。
实际上,Retrofit在生成KaolaApi对象的过程中就是这样处理的,下面是Retrofit#create()的代码:
|
|
显然是使用了Java动态代理的方法,到这里可以清晰看到Retrofit的总体过程:在loadServiceMethod中解析http请求参数,然后利用解析出的参数生成Request,在调用okhttp去真正进行请求,最后利用CallAdapter解析结果。
为了让大家先有一个整体的认识,这里先给出一个核心类的UML图:
4.doSomethingBefore–>parseParameters()
进入loadServiceMethod()方法中,代码如下:
|
|
这里就是如如果在缓存中没有找到,就需要生成一个ServiceMethod对象,先进入Builder()中:
|
|
代码很简单,就是获取方法注解和参数类型,以及参数注解。
build()的代码如下:
|
|
其中parseMethodAnnotation就是解析方法注解的:
|
|
显然,其中主要就是解析HTTP Method和头部参数等。而parameterHandlers[p]=parseParameter(p,parameterType,parameterAnnotations);就是用来解析Http Method和头部参数等之外的接口参数。
5.okhttp执行请求
在解析号参数后,就需要生成Request了,那在哪里生成了呢?
回到Retrofit#create()中来,注意下面这句代码:
|
|
我们进入OkHttpCall中,代码如下:
|
|
显然它完整地实现了Call接口,其中Request request();方法就是用于生成Request的,而execute()方法是用于同步执行Http请求,enqueue(Callback
在接口Call的定义中清楚的注明了各方法的作用:
|
|
再回到OkHttpCall类中,可以明显的看出来它是一个代理类,因为真正执行Http请求的是一个okhttp3.Call对象。那它是如何生成的呢,在request()方法中有return (rawCall = createRawCall()).request();这句,进入到createRawCall()中,代码如下:
|
|
可以看到在serviceMethod.toRequest()中生成了Request对象.其代码如下:
|
|
显然就是将刚刚解析出的httpMethod,baseUrl,relativeUrl,headers,contentType,hasBody,isFormEncodes,parameterHandlers等参数构造出一个Request对象.
回到createRawCall()方法中,那serviceMethod中的callFactory是什么呢?
在ServiceMethod#Builder()中可以看到是来自Retrofit中,我们看一下Retrofit#build()代码:
|
|
可以看到,除非用户自己指定了callFactory,否则默认的是一个OkHttpClient对象。
那OkHttpCall对象何时会被调用呢?
回到Retrofit中,剩下的代码只有return serviceMethod.callAdapter.adapt(okHttpCall);所以肯定在这里面调用到了。
先看一下CallAdapter的定义:
|
|
CallAdapter本身的方法很简单,就是一个返回结果类型方法和适配方法(所谓适配,就是将A类型转换为B类型),重点是里面的工厂类,后面有多个类是实现CallAdapter.Factory的.
serviceMethod中的callAdapter来自Retrofit,那Retrofit中的callAdapter是如何来的呢?
注意到Retrofit.Builder#addCallAdapterFactory()方法,代码如下:
|
|
显然用户可指定,那如果用户没有指定,有默认的吗?
注意到Retrofit.Builder#build()中以下代码:
|
|
显然,如果没有指定的话,有defaultCallAdapterFactory,进入该方法:
|
|
显然,如果用户指定了callbackExecutor,则使用ExecutorCallAdapterFactory,否则使用DefaultCallAdapterFactory.INSTANCE;
先看ExecutorCallAdapterFactory的代码:
|
|
显然,OkHttpCall对象在其中作为delegate存在,OkHttpCall对象的调用也是在其中完成的。
再看以下DefaultCallAdapterFactory的代码:
|
|
显然,除了对返回类型进行了处理之外,call是原样返回的。
现在一般配合rxjava使用,注意到KaolaClient中的以下代码:
|
|
下面我们看一下RxJavaCallAdapterFactory的代码:
|
|
注意到CallOnSubscribe中的call()方法,里面的 Response
对比以下,为什么ExecutorCallAdapterFactory()中既有execute()也有enqueue()方法,而RxJavaCallAdapterFactory中却只调用了OkHttpCall的execute()方法呢?
原因是rxjava调用execute()在工作线程执行后,有一套自己的机制将结果交到观察者线程。
6.doSomethingAfter()–>Callback.OnResponse(),Observable.subscribe()
后处理是利用Converter的实现类来完成的,先看一下Converter的定义:
|
|
接口非常简单,就是一个转换方法和一个工厂类。
实现Converter.Factory的有GsonConverterFactory和JacksonConverterFactory,这里只分析GsonConverterFactory:
|
|
主体就是利用gson将Response数据转换为指定的类型,不再赘述。