网络开源框架OkHttp和Retrofit总结分享
Android为我们提供了两种HTTP交互的方式:HttpURLConnection和 Apache HTTP Client,虽然两者都支持HTTPS,流的上传和下载,配置超时,IPv6和连接池,已足够满足我们各种HTTP请求的需求。但更高效的使用HTTP可以让您的应用运行更快、更节省流量。而OkHttp库就是为此而生。
OKHttp是一款高效的HTTP库,支持连接同一地址的链接共享同一个socket,通过连接池来减小响应延迟,还有透明的GZIP压缩,请求缓存等优势。OkHttp存在以下的特点:
① 支持HTTP2/SPDY
② socket自动选择最好路线,并支持自动重连
③ 拥有自动维护的socket连接池,减少握手次数
④ 拥有队列线程池,轻松写并发
⑤ 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
⑥ 基于Headers的缓存策略
OKHttp源码位置https://github.com/square/okhttp
支持Android2.3及其以上版本,要求java JDK1.7以上
可以通过下载jar包直接导入工程地址如下下载地址
或者通过构建的方式导入
compile 'com.squareup.okhttp:okhttp:2.4.0'
在向网络发起请求的时候,我们最常用的就是GET和POST,下面就来看看如何使用 。
⑴ GET请求
//得到OKHttpClient对象
OkHttpClient okHttpClient=new OkHttpClient();
//得到Request对象
Request request = new Request.Builder()
.url("https://www.baidu.com/")
.build();
//OkhttpClient#newCall()得到Call对象
Call call=okHttpClient.newCall(request);
//Call#execute()同步请求网络
Response response = call.execute();
//Call#enqueue()异步请求网络
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("fail",e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException{
Log.e("success",response.body().toString());
}
});
OkHttp支持同步和异步的方式来操作网络请求,如上面的execcute方法和enqueue方法所示。同步方法返回的response以及异步方法回调
的参数是response
,一般情况下,比如我们希望获得返回的字符串,可以通过response.body().string()
获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes()
;如果你想拿到返回的inputStream
,则调用response.body().byteStream()
。
在使用同步和异步方式来操作网络请求时,需要注意两点:
① 由于Android本身是不允许在UI线程做网络请求操作的,因此在利用同步方式来操作网络请求时,我们需要自己开启一个线程来进行操作。
② 异步方式操作网络请求的异步回调是在非UI线程中,因此如果有更新UI的操作记得用Handler或者其他方式。
⑵ POST请求
OkHttpClientokHttpClient=new OkHttpClient();
//构建RequestBody对象,调用add()方法构建我们的键值对
RequestBody body=new FormBody.Builder()
.add("district","%E5%8C%97%E4%BA%AC")
.build();
//在构建Request对象时,调用post方法,传入RequestBody对象
Request request=new Request.Builder()
.url("https://www.baidu.com/")
.post(body)
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback(){
@Override
public void onFailure(Call call, IOException e) {
Log.e("fail",e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException{
Log.e("success",response.body().toString());
}
});
可以看出来,Post请求需要通过构建RequestBody对象来将post参数都是传到Request里面。Post请求因此最的调用方式也和GET方式一样,也支持同步和异步的网络请求方式。
⑶ 缓存设置
在网络请求中,缓存技术是一项应用比较广泛的技术,需要对请求过的网络资源进行缓存,而okhttp也支持这一技术,也使用十分方便,只需调用OkHttpClient类中的setCache方法即可。
File sdcache =getExternalCacheDir();
int cacheSize = 10 * 1024 * 1024; //10 MiB
client.setCache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
new Thread(new Runnable(){
@Override
public void run() {
try {
execute();
} catch(Exception e) {
e.printStackTrace();
}
}
}).start();
但有时候即使在有缓存的情况下我们依然需要去后台请求最新的资源(比如资源更新了)这个时候可以使用强制走网络来要求必须请求网络数据。
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
request =request.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build();
Response response =client.newCall(request).execute();
同样的我们可以使用 FORCE_CACHE 强制只使用缓存的数据,但如果请求必须从网络获取才有数据,但又使用了FORCE_CACHE 策略就会返回504错误。
⑷ 取消请求
网络操作中,经常会使用到对请求的cancel操作,OkHttp的也提供了这方面的接口,call的cancel操作。使用Call.cancel()可以立即停止掉一个正在执行的call。如果一个线程正在写请求或者读响应,将会引发IOException,同时可以通过Request.Builder.tag(Object tag)给请求设置一个标签,并使用OkHttpClient.cancel(Objecttag)来取消所有带有这个tag的call。但如果该请求已经在做读写操作的时候,cancel是无法成功的,会抛出IOException异常。
当我们用OkHttpClient.newCall(request)
进行execute/enenqueue
时,实际是将请求Call
放到了Dispatcher
中,OkHttp使用Dispatcher进行线程分发,它有两种方法,一个是普通的同步单线程;另一种是使用了队列进行并发任务的分发(Dispatch)与回调,我们下面主要分析第二种,也就是队列这种情况,这也是okhttp能够竞争过其它库的核心功能之一。
使用OkHttp3发送Http请求并获得响应的过程大体为:
1.创建OkHttpClient对象。OkHttpClient为网络请求执行的一个中心,它会管理连接池,缓存,SocketFactory,代理,各种超时时间,DNS,请求执行结果的分发等许多内容。
2.创建Request对象。Request用于描述一个HTTP请求,比如请求的方法是"GET"还是"POST",请求的URL,请求的header,请求的body,请求的缓存策略等。
3.利用前面创建的OkHttpClient对象和Request对象创建Call对象。Call是一次HTTP请求的Task,它会执行网络请求以获得响应。OkHttp中的网络请求执行Call既可以同步进行,也可以异步进行。调用call.execute()将直接执行网络请求,阻塞直到获得响应。而调用call.enqueue()传入回调,则会将Call放入一个异步执行队列,由ExecutorService在后台执行。
4.执行网络请求并获取响应。
从上述的请求流程中可以看出,OkHttpClient同步请求和异步请求调用的接口是不一样的,但它们最后都是殊途同归地走到使用拦截器链来进行网络请求。
我们从源码的角度来分析它是如何实现的,同步请求较简单,如下所示:
public Response execute() throws IOException{
synchronized (this) {
if (executed)throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
//使用拦截器链来进行网络请求
Response result =getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally{
client.dispatcher().finished(this);
}
}
通过调用RealCall.execute()同步执行Http请求的过程大体为:
1. 调用client.dispatcher().executed(this)向client的dispatcher注册当前Call。
2. 调用getResponseWithInterceptorChain()执行网络请求并获得响应。
3. 调用client.dispatcher().finished(this)向client的dispatcher注销当前Call。
异步请求比较复杂,Call对外提供的接口的实现代码如下所示:
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed)throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
可以看出,和同步不同的是,同步传入enqueue方法的参数是Call,异步传入的是AsyncCall,这个是什么呢,这个是Call里面的一个内部类,而且是一个继承了Runnable的内部类,我们先来看看这个Dispatcher类中的enqueue是怎么操作的。
synchronized void enqueue(AsyncCall call) { //判断当前运行的线程是否超过最大线程数(64
),以及请求同一host是否要超过相同请求同时存在的最大数目
(5)
if (runningAsyncCalls.size()< maxRequests && runningCallsForHost(call) < maxRequestsPerHost){
runningAsyncCalls.add(call);
//将请求放到线程池里运行
executorService().execute(call);
} else{
//不满足运行条件放到后备队列里
readyAsyncCalls.add(call);
}
}
再来看看AsyncCall 的run里面的代码:
protected void execute() {
boolean signalledCallback = false;
try {
//使用拦截器链来进行网络请求
Response response =getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback= true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else{
signalledCallback= true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOExceptione) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callbackfailure for " +toLoggableString(), e);
} else{
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//完成请求,调用finish,从队列中清空
client.dispatcher().finished(this);
}
}
}
通过调用RealCall.enqueue()异步执行Http请求的过程则为,创建AsyncCall并将之丢给client的dispatcher。而在RealCall.AsyncCall的execute()中执行Http请求的过程与RealCall.execute()中的过程有些类似:
从上面对同步和异步请求流程的代码分析结果可以看出,都会调用拦截器链进行网络请求,即调用getResponseWithInterceptorChain方法来进行网络请求并得到请求结果。下面我们来看看getResponseWithInterceptorChain方法的具体实现:
private Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List
interceptors.addAll(client.interceptors());//自定义拦截器
interceptors.add(retryAndFollowUpInterceptor); //重试拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));//桥接拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));//缓存拦截器
interceptors.add(new ConnectInterceptor(client)); //连接拦截器
if (!retryAndFollowUpInterceptor.isForWebSocket()){
interceptors.addAll(client.networkInterceptors());
}//用户预定义的网络拦截器
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));//服务拦截器
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
上述的代码使用了拦截器链条,这里边重点说一下拦截器链条的实现以及作用。
Interceptor本身的官方文档解释:Interceptors area powerful mechanism that can monitor, rewrite, and retry calls.翻译过来就是拦截器可以用来监控,重试,重写请求的机制。通常情况下拦截器用来添加,移除或者转换请求或者回应的头部信息。拦截器是OkHttp中强大的流程装置,它可以用来监控log,修改请求,修改结果。
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Responseproceed(Request request) throwsIOException;
Connection connection();
}
}
拦截器接口中有intercept(Chain chain)方法,同时返回Response。所谓拦截器更像是AOP设计的一种实现。
在OkHttp中,内部维护了一个Interceptors
的List,通过InterceptorChain
进行多次拦截修改操作。RealInterceptorChain类中的proceed方法的代码如下所示:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) throws IOException{
if (index>= interceptors.size()) thrownew AssertionError();
calls++;
// If we already have a stream, confirm that the incomingrequest will use it.
if (this.httpCodec != null &&!sameConnection(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is theonly call to chain.proceed().
if (this.httpCodec != null && calls> 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its requiredcall to chain.proceed().
if (httpCodec!= null && index + 1
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response== null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
在RealInterceptorChain.proceed()中,除了对状态及获取的reponse做检查之外,最主要的事情即是构造新的RealInterceptorChain对象,获取对应Interceptor,并调用Interceptor的intercept(next)了。在这里,index充当迭代器或指示器的角色,用于指出当前正在处理的Interceptor。
Interceptor和RealInterceptorChain配合,实现类递归的形式,从最上层开始(Interceptor列表第一个)一层一层获取结果,而且每层对下层返回的结果又可以做处理,缓存包装等,实现了类似AOP的模式。
总结一下这几个Interceptor的职责:
RetryAndFollowUpInterceptor--->创建StreamAllocation对象,处理http的redirect,出错重试。对后续Interceptor的执行的影响:修改request及StreamAllocation。
BridgeInterceptor-------------->补全缺失的一些http header。对后续Interceptor的执行的影响:修改request。
CacheInterceptor-------------->处理http缓存。对后续Interceptor的执行的影响:若缓存中有所需请求的响应,则后续Interceptor不再执行。
ConnectInterceptor------------>借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响:创建了httpStream和connection。
CallServerInterceptor----------->处理IO,与服务器进行数据交换。对后续Interceptor的执行的影响:为Interceptor链中的最后一个Interceptor,没有后续Interceptor。
完整的流程图:
⑴ Biluder模式
Okhttp大量使用builder设计模式:
Request类使用了builder模式,每个http请求包含URL, request method(GET/POST等等),headers, requestBody。Request通过Request.Builder对象设置各种属性,最终调用build()生成Request对象。
Response类使用了builder模式,包含了response code(200, 401等等), headers ,response body。
OkHttpClient类使用了builder模式,可以是设置connectTimout, readTimeout, writeTimout的时间,设置Dispatcher, Dns, Cache,Interceptors,NetworkInterceptors等等。
⑵ 拦截器模式
OkHttp使用了拦截器模式,利用拦截器模式可以对请求进行监控、对请求的Resquest/请求返回的Response进行重写以及请求重试。通过拦截器可以添加,移除,替换请求头。也可以改变请求的请求体。相对应地,拦截器可以重写响应头和改变响应体。针对Request和Response的进行切面处理。
⑶ 门面模式
从OkHttpClient类的整体设计来看,它采用门面模式来。client知晓子模块的所有配置以及提供需要的参数。client会将所有从客户端发来的请求委派到相应的子系统去。
参考资料:
http://www.jianshu.com/p/230e2e2988e0
http://blog.csdn.net/xiandan87/article/details/52099290
http://www.jianshu.com/p/2710ed1e6b48
http://blog.csdn.net/qq_31694651/article/details/52463078
http://www.tuicool.com/articles/jUryY3Q
二、Retrofit框架
Retrofit与okhttp共同出自于Square公司,retrofit就是对okhttp做了一层封装。把网络请求都交给给了Okhttp,我们只需要通过简单的配置就能使用retrofit来进行网络请求了。Retrofit框架存在的优势:
① Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂;
② Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意;
④ Retrofit支持同步和异步执行,使得请求变得异常简单,只要调用enqueue/execute即可完成;
④ Retrofit更大自由度的支持我们自定义的业务逻辑,如自定义Converters。
⒈ Retrofit框架使用
1.1 jar包导入
导包:
compile'com.squareup.retrofit2:retrofit:2.0.0-beta4'//Retrofit2所需要的包
compile'com.squareup.retrofit2:converter-gson:2.0.0-beta4'//ConverterFactory的Gson依赖包
compile 'com.squareup.retrofit2:converter-scalars:2.0.0-beta4'//ConverterFactory的String依赖包
*这里需要值得注意的是导入的retrofit2包的版本必须要一致,否则就会报错。
1.2 代码使用
首先,我们需要创建一个java接口,用于存放请求方法的:
然后逐步在该方法中添加我们所需要的方法(按照请求方式):
⑴ Get请求方式
① 直接通过URL获取网络内容:
在这里我们定义了一个listRepos()的方法,通过@GET注解标识为get请求,请求的URL为“users/octocat/repos”。
然后看看Retrofit是怎么调用的,代码如下:
代码解释:首先获取Retrofit对象,然后通过动态代理获取到所定义的接口,通过调用接口里面的方法获取到Call类型返回值,最后进行网络请求操作(这里不详细说明Retrofit 实现原理,后面会对它进行源码解析),这里必须要说的是请求URL的拼接:在构建Retrofit对象时调用baseUrl所传入一个String类型的地址,这个地址在调用service.listRepos()时会把@GET(“users/octocat/repos”)的URL拼接在尾部。
② 动态获取URL地址:@Path
我们再上面的基础上进行修改,如下:
这里在Get注解中包含{user},它所对应的是@Path注解中的“user”,它所标示的正是String user,而我们再使用Retrofit对象动态代理的获取到GitHubService,当调用listRepos时,我们就必须传入一个String类型的User,如:
如上代码,其他的代码都是不变的,而我们只需要使用@Path注解就完全的实现了动态的URL地址了,是不是很方便呢,这还不算什么,通常情况下,我们去获取一些网络信息,因为信息量太大,我们会分类去获取,也就是携带一些必要的元素进行过滤,那我们该怎么实现呢?其实也很简单,因为Retrofit已经为我们封装好了注解,请看下面(官网实例):
③ 动态指定条件获取信息:@Query
我们只需要使用@Query注解即可完成我们的需求,在@Query(“sort”)中,short就好比是URL请求地址中的键,而它说对应的String sort中的sort则是它的值。
但是我们想,在网络请求中一般为了更精确的查找到我们所需要的数据,过滤更多不需要的无关的东西,我们往往需要携带多个请求参数,当然可以使用@Query注解,但是太麻烦,很长,容易遗漏和出错,那有没有更简单的方法呢,有,当然后,我们可以直接放到一个map键值对中:
④ :动态指定条件组获取信息:@QueryMap
使用@QueryMap注解可以分别地从Map集合中获取到元素,然后进行逐个的拼接在一起。
⑵ POST请求方式
稍微了解点Http的同学们,可能都会知道:相对于get请求方式把数据存放在uri地址栏中,post请求传输的数据时存放在请求体中,所以post才能做到对数据的大小无限制。而在Retrofit中,它又是怎么使用的呢?请看下面:
① 携带数据类型为对象时:@Body
当我们的请求数据为某对象时Retrofit是这么处理使用的:
首先,Retrofit用@POST注解,标明这个是post的请求方式,里面是请求的url;
其次,Retrofit仿照http直接提供了@Body注解,也就类似于直接把我们要传输的数据放在了body请求体中,这样应用可以更好的方便我们理解。
来看下应用:
这样我们直接把一个新的User对象利用注解@Body存放在body请求体,并随着请求的执行传输过去了。
② 携带数据类型为表单键值对时:@Field
当我们要携带的请求数据为表单时,通常会以键值对的方式呈现,那么Retrofit也为我们考虑了这种情况,它首先用到@FormUrlEncoded注解来标明这是一个表单请求,然后在我们的请求方法中使用@Field注解来标示所对应的String类型数据的键,从而组成一组键值对进行传递。
从上面的代码使用可以看出,与使用OkHttp框架不同的是,使用OkHttp必须要先创建一个Request对象,而Retrofit框架只要创建一个接口就可以了,它是如何做到?答案就是动态代理。
Java动态代理就是Java开发给了开发人员一种可能:当你要调用某个类的方法前,插入你想要执行的代码
比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱。这么简单的一句话,我相信可以把一个不懂技术的人也讲明白Java动态代理是什么东西了。
⒉ Retrofit源码解析实现原理
首先先看一下Retrofit2标准示例
由上面我们基本可以看出,Retrofit是通过构造者模式创建出来的,那么我们就来看看Builder这个构造器的源码:
源码讲解:
1、当我们使用new Retrofit.Builder()来创建时,在Builder构造器中,首先就获得当前的设备平台信息,并且把内置的转换器工厂(BuiltInConverters)加添到工厂集合中,它的主要作用就是当使用多种Converters的时候能够正确的引导并找到可以消耗该类型的转化器。
2、从我们的基本示例中看到有调用到.baseUrl(BASE_URL)这个方法,实际上每当使用Retrofit时,该方法都是必须传入的,并且还不能为空,从源码中可以看出,当baseUrl方法传进的参数来看,如果为空的话将会抛出NullPointerException空指针异常。
3、addConverterFactory该方法是传入一个转换器工厂,它主要是对数据转化用的,请网络请求获取的数据,将会在这里被转化成我们所需要的数据类型,比如通过Gson将json数据转化成对象类型。
4、从源码中,我们看到还有一个client方法,这个是可选的,如果没有传入则就默认为OkHttpClient,在这里可以对OkHttpClient做一些操作,比如添加拦截器打印log等
5、callbackExecutor该方法从名字上看可以得知应该是回调执行者,也就是Call对象从网络服务获取数据之后转换到UI主线程中。
6、addCallAdapterFactory该方法主要是针对Call转换了,比如对Rxjava的支持,从返回的call对象转化为Observable对象。
7、最后调用build()方法,通过new Retrofit(callFactory,baseUrl, converterFactories, adapterFactories, callbackExecutor,validateEagerly);构造方法把所需要的对象传递到Retrofit对象中。
ok,当我们通过Builder构造器构造出Retrofit对象时,然后通过Retrofit.create()方法是怎么把我们所定义的接口转化成接口实例的呢?来看下create()源码:
从create()的源码中可以看出它使用了动态代理,动态代理其实已经封装的很简单了,主要使用newProxyInstance()方法来返回一个类的代理实例,其中它内部需要传递一个类的加载器,类本身以及一个InvocationHandler处理器,主要的动作都是在InvocationHandler中进行的,它里面只有一个方法invoke()方法,每当我们调用代理类里面的方法时invoke()都会被执行,并且我们可以从该方法的参数中获取到所需要的一切信息,比如从method中获取到方法名,从args中获取到方法名中的参数信息等。
Retrofit使用的动态代理主要干了以下几件事情:
首先,通过method把它转换成ServiceMethod ;
然后,通过serviceMethod,args获取到okHttpCall 对象;
最后,再把okHttpCall进一步封装并返回Call对象。
下面从代码层面来分析下上面做的几件事情。
1、将method把它转换成ServiceMethod
loadServiceMethod源码方法中非常的好理解,主要就是通过ServiceMethod.Builder()方法来构建ServiceMethod,并把它给缓存取来,以便下次可以直接回去ServiceMethod。那下面我们再来看看它是怎么构建ServiceMethod方法的:
首先在Builder()中初始化一些参数,然后在build()中返回一个new ServiceMethod<>(this)对象。
下面来详细的解释下build()方法,完全理解了该方法则便于理解下面的所有执行流程。
① 构建CallAdapter对象,该对象将会在第三步中起着至关重要的作用。
现在我们先看看它是怎么构建CallAdapter对象的:createCallAdapter()方法源码如下:
在createCallAdapter方法中主要做的是事情就是获取到method的类型和注解,然后调用retrofit.callAdapter(returnType,annotations);方法。
转到Retrofit中nextCallAdapter()中,在for 循环中分别从adapterFactories中来获取CallAdapter对象,但是adapterFactories中有哪些CallAdapter对象呢,这就需要返回到构建Retrofit对象中的Builder 构造器中查看了
从上面的代码中可以看到,不管有没有通过addCallAdapterFactory添加CallAdapter,adapterFactories集合至少都会有一个ExecutorCallAdapterFactory对象。当我们从
adapterFactories集合中回去CallAdapter对象时,那我们都会获得ExecutorCallAdapterFactory这个对象。而这个对象将在第三步中和后面执行同步或异步请求时起着至关重要的作用。
② 构建responseConverter转换器对象,它的作用是寻找适合的将网络返回数据转化为你所需要的类型
该对象的构建和构建CallAdapter对象的流程基本是一致的,这里就不在赘述。同学们可自行查看源码。
2、通过serviceMethod, args获取到okHttpCall 对象
第二步相对比较简单,就是对象传递:
3、把okHttpCall进一步封装并返回Call对象
这一步也是一句话 returnserviceMethod.callAdapter.adapt(okHttpCall);但是想理解清楚必须先把第一步理解透彻,通过第一步我们找得到serviceMethod.callAdapter就是ExecutorCallAdapterFactory对象,那么调用.adapt(okHttpCall)把okHttpCall怎么进行封装呢?看看源码:
从上面的源码可以看出调用adapt方法后返回了ExecutorCallbackCall实例,ExecutorCallbackCall实例是怎么应用的我们在发起网络请求的时候讲解。
从上面的分析可以知道,当我们得到接口的代理实例之后,通过代理接口调用里面的方法,就会触发InvocationHandler对象中的invoke方法,从而完成上面的三个步骤并且返回一个Call对象,通过Call对象就可以去完成我们的请求了。
Retrofit为我们提供两种请求方式,一种是同步,一种是异步。我们这里就以异步方式来讲解:
从上面我们可以看到enqueue方法中有一个回调函数,回调函数里面重写了两个方法分别代表请求成功和失败的方法,但是我们想知道它是怎么实现的原理呢?那么请往下面看:
在上面获取接口的代理实例时,通过代理接口调用里面的方法获取一个Call对象,我们上面也分析了其实这个Call对象就是ExecutorCallbackCall,那么我们来看看它里面是怎么实现的?
在ExecutorCallbackCall类中,封装了两个对象一个是callbackExecutor,默认的CallBackExecutor是把现在运行的线程切换到主线程中去,一个是delegate对象,这个对象就是真真正正的执行网络操作的对象,由上述的代码的分析可以看出 delegate 就是okHttpCall对象。
我们接下来再看看okHttpCall是怎么执行异步网络请求的:
从上面代码中,我们很容易就看出,其实它就是这里面封装了一个okhttp3.Call,直接利用okhttp进行网络的异步操作,至于okhttp是怎么进行网络请求的我们就不再这里讲解了。
在调用OkHttpCall的enqueue时,可以看到Callback中会对结果Response(okHttp中的)进行进一步解析,然后将解析后的结果通过Retrofit中定义的callback进行返回,返回的结果为Response。我们来看下parseResponse方法的代码:
当请求发生错误,获取204情况没有实体返回时,自然不用对实体进行转化;而对于实体的转换是通过最初定义的转换器来对返回结果进行转换的;然后将转换后的结果同原始okHttp返回的Response封装成一个Response进行返回;
以GsonConverterFactory为例,来看下它是如何进行转换的:
从上面可以看出它的转换函数convert逻辑也较为简单,将工作交给gson就可以了。
至此,Retrofit的整个网络请求的执行流程都讲解完毕了。
Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求。
Retrofit中接口设计的恰到好处,在你创建Retrofit
对象时,让你有更多更灵活的方式去处理你的需求,比如使用不同的Converter
、使用不同的CallAdapter
。
Retrofit中众多的接口,更加深入的理解面向接口的编程方法,这个写代码就是好的代码就是依赖接口而不是实现。
参考文章:
http://blog.csdn.net/guiman/article/details/51480497
http://www.jianshu.com/p/c1a3a881a144
http://blog.csdn.net/woliuyunyicai/article/details/51479011
http://www.cnblogs.com/krislight1105/p/5492749.html
本文由100IME操大神分享