OKHttp简介
OkHttp是一款优秀的HTTP框架,它支持get请求和post请求,支持基于Http的文件上传和下载,支持加载图片,支持下载文件透明的GZIP压缩,支持响应缓存避免重复的网络请求,支持使用连接池来降低响应延迟问题。OkHttp由Square公司开发,是目前Android最热门的网络框架之一。
OKHttp特点
(1)支持HTTP2/SPDY
(2)socket自动选择最好路线,并支持自动重连
(3)拥有自动维护的socket连接池,减少握手次数
(4)拥有队列线程池,轻松写并发
(5)拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩)基于Headers的缓存策略
为什么要用OkHttp?
目前Android开发中,主要的网络框架有HttpClient、Volley、HttpURLConnection、OkHttp。其中Android早就不推荐httpclient,5.0之后干脆废弃,6.0删除了HttpClient。所以HttpClient不考虑。Volley框架现在也已经不再升级了,故目前考虑使用的有、HttpURLConnection及OkHttp。相对HttpURLConnection,OkHttp使用更加便捷及灵活,且第三方社区活跃,相关资料齐全,成熟稳定性高。OkHttp也得到了官方的认可,并在不断优化更新,所以建议应用优先选择OkHttp作为网络框架。
为什么OKHttp很重要?
在Android网络请求库中,Retrofit是当下最热的一个网络请求库,Retrofit和OKHttp对比如下所示,Retrofit网络请求的工作本质上是OkHttp完成,而Retrofit仅负责网络请求接口的封装。OkHttp的底层网络实现是什么?
OkHttp使用okio进行io的操作。okio是由square公司开发的,它补充了java.io和java.nio的不足,以便能够更加方便,快速的访问、存储和处理你的数据。OKHttp底层也是用该库作为支持。而且okio使用起来很简单,减少了很多io操作的基本代码,并且对内存和CPU使用做了优化。没有依赖其他的关于Http实现的库,底层使用了Socket,自己实现了Http1.X及2.X的协议。
OKHtttp缓存机制
HTTP缓存机制
HTTP报文就是客户端和服务器之间通信时发送及其响应的数据块,报文信息主要分为两部分:
(1)包含属性的头部(header),附加信息(cookie,缓存信息等),与缓存相关的规则信息,均包含在header中;
(2)包含数据的主体部分(body)HTTP请求真正想要传输的部分;
缓存的几种方法:
(1)设置资源有效时间,超过指定时间重新想服务器获取资源,小于指定时间直接使用缓存;
(2)通过告知客户端资源最后修改时间,让客户端对比资源最后修改时间来确认是否使用缓存;
(3)通过告知客户端当前资源在服务器的唯一标识,客户端根据此标识确认资源是否有变换,来确认是否使用缓存。
OKHTTP的缓存的实现
原理
(1)OKHttp的网络缓存是基于HTTP缓存机制;
(2)使用DiskLruCache的缓存策略,通过LinkedHashMap实现LRU缓存算法,图片加载框架ImageLoader也是通过LinkedHashMap实现缓存机制。
注意事项:
(1)目前只支持GET,其他请求方式需要自己实现。
(2)需要服务器配合,通过head设置相关头来控制缓存
(3)创建OkHttpClient时候需要配置Cache
流程:
(1)如果配置了缓存,则从缓存中取出(可能为null)
(2)获取缓存的策略.
(3)监测缓存
(4)如果禁止使用网络(比如飞行模式),且缓存无效,直接返回
(5)如果缓存有效,使用网络,不使用网络
(6)如果缓存无效,执行下一个拦截器
(7)本地有缓存、根据条件判断是使用缓存还是使用网络的response
(8)把response缓存到本地
OKHTTP的缓存的实现位于CacheInterceptor类中,具体内容请参考:
OKHttp源码解析(六)--中阶之缓存基础
OKHttp源码解析(七)--中阶之缓存机制
OKHttp使用
步骤一:gradle引入库:implementation 'com.squareup.okhttp3:okhttp:(insert latest version)'
步骤二:初始化OkHttpClient对象
OkHttpClient client;
private void initClient(){
client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build();
}
步骤三:
同步请求
public void okHttpSync() {
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
if (response.isSuccessful()) {
System.out.println("response.code()==" + response.code());
System.out.println("response.heard()==" + response.headers());
System.out.println("response.message()==" + response.message());
System.out.println("res==" + response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
异步请求
public void okHttpAsync() {
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("url==" + call.request().url());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
System.out.println("response.code()==" + response.code());
System.out.println("response.heard()==" + response.headers());
System.out.println("response.message()==" + response.message());
System.out.println("res==" + response.body().string());
}
}
});
}
OKHTTP源码流程分析
OKHTTP同步请求流程分析
OkHttpClient client;
private void initClient(){
client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build();
}
public void okHttpSync() {
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
if (response.isSuccessful()) {
System.out.println("response.code()==" + response.code());
System.out.println("response.heard()==" + response.headers());
System.out.println("response.message()==" + response.message());
System.out.println("res==" + response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
(1)如上代码所示,先是new了一个OKHttpClient对象。OKhttp里面包含了很多对象,其实OKhttp的很多功能模块都包装进这个类,让这个类单独提供对外的API,这种外观模式的设计十分的优雅。外观模式。
(2)然后又new了一个Request对象,OKHttp的封装类Request和Response为了应用程序编程方便,会把一些常用的Header信息专门提取出来,作为局部变量。比如contentType,contentLength,code,message,cacheControl,tag...它们其实都是以name-value对的形势,存储在网络请求的头部信息中。
(3)然后调用OKHttpClient的newcall方法创建Call对象(RealCall对象),Call是HTTP请求任务封装,可以说我们能用到的操纵基本上都定义在这个接口里面了,
所以也可以说这个类是OKHttp类的核心类了。我们可以通过Call对象来操作请求了。代码如下所示:
OKHttpClient.java
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
(4)执行RealCall.execute()方法开始网络请求
@Override
public Response execute() throws IOException {
try {
//将该RealCall添加到runningSyncCalls队列中。
client.dispatcher().executed(this);
//开始请求,并返回结果。
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//结束请求,从runningSyncCalls队列中移除该RealCall。
client.dispatcher().finished(this);
}
}
(5)开始执行getResponseWithInterceptorChain()方法
Response getResponseWithInterceptorChain() throws IOException {
List interceptors = new ArrayList<>();
//添加开发者应用层自定义的Interceptor
interceptors.addAll(client.interceptors());
//这个Interceptor是处理请求失败的重试,重定向
interceptors.add(retryAndFollowUpInterceptor);
//这个Interceptor工作是添加一些请求的头部或其他信息
//并对返回的Response做一些友好的处理(有一些信息你可能并不需要)
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//这个Interceptor的职责是判断缓存是否存在,读取缓存,更新缓存等等
interceptors.add(new CacheInterceptor(client.internalCache()));
//这个Interceptor的职责是建立客户端和服务器的连接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//添加开发者自定义的网络层拦截器
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
//一个包裹这request的chain
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
//把chain传递到第一个Interceptor手中
return chain.proceed(originalRequest);
}
(6)执行RealInterceptorChain.proceed()方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
// Call the next interceptor in the chain.
//创建一个RealInterceptorChain实例
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//取出下一个拦截器,如果有自定义Interceptor,先取出自定义Interceptor,
//如果没有则先取出RetryAndFollowUpInterceptor,然后执行RetryAndFollowUpInterceptor.intercept()方法
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
return response;
}
(7)如果没有自定义Interceptor,则执行先RetryAndFollowUpInterceptor.intercept()方法
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
while (true) {
try {
//继续调用RealInterceptorChain执行下一个拦截器的intercept方法()
response = realChain.proceed(request, streamAllocation, null, null);
}
return response;
}
}
可以看出在RetryAndFollowUpInterceptor.intercept()会继续调用RetryAndFollowUpInterceptor.intercept()方法,然后继续执行下一个Interceptor.intercept()方法,直到RealCall.getResponseWithInterceptorChain()方法中的interceptor执行完毕,最后一个Interceptor是CallServerInterceptor,在CallServerInterceptor.intercept()方法中将response返回给上一个interceptor(),上一级再返回给上上一级,依次类推返回给第一个interceptor,这时候就返回到RealCall.execute()方法中
@Override
public Response execute() throws IOException {
try {
client.dispatcher().executed(this);
//开始请求,并返回结果
//最后拦截器把response返回给get请求的返回值
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//结束请求,从runningSyncCalls队列中移除该RealCall。
client.dispatcher().finished(this);
}
}
至此同步请求大体流程已执行完毕,最后执行client.dispatcher().finished(this),从runningSyncCalls队列中移除该RealCall。
大体流程如下图所示:
其中拦截器执行流程如下图所示:
每个拦截器主要做两件事,第一是对Request进行处理,第二是对Response处理,对Request是处理时顺序执行的,对Response处理时倒序执行的,每个拦截器都有自己的作用,根据自己的职责和任务对Request和Response做响应的处理,如CacheInterceptor拦截器,根据Request请求报文的头部(header)有关缓存的设置,来判断是否使用缓存、判断缓存是否存在,读取缓存,更新缓存等等,最终将Response返回。下图有助于理解拦截器
OKHTTP异步请求流程分析
public void okHttpAsync() {
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("url==" + call.request().url());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
System.out.println("response.code()==" + response.code());
System.out.println("response.heard()==" + response.headers());
System.out.println("response.message()==" + response.message());
System.out.println("res==" + response.body().string());
}
}
});
}
(1)异步请求调用RealCall.enqueue()方法
@Override
public void enqueue(Callback responseCallback) {
//先new一个AsyncCall,然后调用Dispatcher.enqueue()方法,
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
(2)NamedRunnable和AsyncCall代码如下
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override
public final void run() {
try {
//执行NamedRunnable时会调用AsyncCall.execute方法;
execute();
} finally {
}
}
protected abstract void execute();
}
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
//responseCallback即我们代码中call.enqueue(new CallBack()),请求完成后回调CallBack响应方法。
AsyncCall(Callback responseCallback) {
this.responseCallback = responseCallback;
}
//执行NamedRunnable时会调用AsyncCall.execute方法;
@Override
protected void execute() {
boolean signalledCallback = false;
try {
//发起请求,并返回结果,和同步请求执行逻辑一致。
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
//请求失败,回调CallBack.onFailure方法,
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
//请求成功,回调CallBack.onResponse方法
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
} finally {
//结束请求
client.dispatcher().finished(this);
}
}
}
AsyncCall.execute方法中的Response response = getResponseWithInterceptorChain();执行流程和同步请求一致,相关内容就不再过多介绍,然后根据返回结果回调我们代码中Callback相关方法。
(3)Dispatcher.enqueue方法如下
private int maxRequests = 64; // 最大并发请求数为64
private int maxRequestsPerHost = 5; //每个主机最大请求数为5
synchronized void enqueue(RealCall.AsyncCall call) {
//如果正在执行的请求小于设定值即64,并且请求同一个主机的request小于设定值即5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//将异步请求添加到执行队列,开始执行请求
runningAsyncCalls.add(call);
//获得当前线程池,没有则创建一个
executorService().execute(call);
} else {
//当前异步请求队列已满,先添加到等待队列中,
//待有异步请求队执行完毕后,再开始执行等待队列中的请求
readyAsyncCalls.add(call);
}
}
//获取线程池,没有的话创建一个
public synchronized ExecutorService executorService() {
if (executorService == null) {
/*1、0:核心线程数量,保持在线程池中的线程数量(即使已经空闲),为0代表线程空闲后不会保留,等待一段时间后停止。
2、Integer.MAX_VALUE:表示线程池可以容纳最大线程数量
3、TimeUnit.SECOND:当线程池中的线程数量大于核心线程时,空闲的线程就会等待60s才会被终止,如果小于,则会立刻停止。
4、new SynchronousQueue():线程等待队列。同步队列,按序排队,先来先服务
5、Util.threadFactory("OkHttp Dispatcher", false):线程工厂,直接创建一个名为OkHttp Dispatcher的非守护线程。
*/
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
(4)其中executorService().execute(call)方法会执行NamedRunnable.run()方法,
NamedRunnable.run()又会执行AsyncCall.execute()方法,AsyncCall.execute方法如下
protected void execute() {
boolean signalledCallback = false;
try {
//发起请求,并返回结果,和同步请求执行逻辑一致。
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
//请求失败,回调CallBack.onFailure方法,
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
//请求成功,回调CallBack.onResponse方法
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
} finally {
//结束请求
client.dispatcher().finished(this);
}
}
执行getResponseWithInterceptorChain()方法并返回结果,根据response来执行callback,所以我们得到了OKHTTP的大体流程,如下图
最后OKHttp整体流程如下所示:
参考资料:
OKHttp源码解析系列文章
Android:手把手带你深入剖析 Retrofit 2.0 源码
OkHttp源码学习随笔
OKHTTP
最后,感谢本文内容所参考文章的作者;