「简学」OKhttp
Okhttp简介
HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽。OkHttp是一个高效的HTTP客户端,它有以下默认特性:
- 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
- 连接池减少请求延时
- 透明的GZIP压缩减少响应数据的大小
- 缓存响应内容,避免一些完全重复的请求
当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP,OkHttp使用现代TLS技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到TLS 1.0。
OkHttp官网地址:http://square.github.io/okhttp/
pom依赖:
com.squareup.okhttp3
okhttp
4.7.2
Okhttp使用
本文使用的OkHttp版本为4.7.2。OkHttp的核心类主要有OkHttpClient,Dispatcher,Call,Request,Response,Interceptor,Chain。其中OkHttpClient是负责管理多个Call的组织者,而每个Call又包含一个Request和Response,并且Call中的回调用于提供响应结果。要完成一次网络请求,我们需要告诉Call需要处理的Request是什么样的,例如它的URL是什么,然后将Call交给OkHttpClient。OkHttpClient仅对本次请求做一些配置,例如指定缓存路径,它会让Dispatcher去决定何时执行Call。而Dispatcher的底层实现就是一个由OkHttp默认实现的线程池,它将最终执行Call中的.run()方法。最后的Interceptor和Chain将用于数据的拦截处理。OkHttp提供两种方式提交网络请求,分别是Call.execute()和Call.enqueue(Callback),前者会阻塞线程,后者加入队列异步执行。通过调用response.body().string()我们可以得到响应的body部分并以String形式返回,但值得注意的是.string()只能调用一次。
创建并配置OkHttpClient
OkHttpClient通过内部的Builder类来创建。
OkHttpClient.Builder默认参数:
public Builder() {
// dispatch。负责分配处理异步任务
this.dispatcher = new Dispatcher();
// 支持的协议,默认支持http2和http1.1
this.protocols = OkHttpClient.DEFAULT_PROTOCOLS;
// 指定socket链接配置。默认:MODERN_TLS和CLEARTEXT
this.connectionSpecs = OkHttpClient.DEFAULT_CONNECTION_SPECS;
// 监听http请求过程中涉及的各种事件。包括:开始请求、dns解析开始/结束、链接开始等等。默认是的Listener
this.eventListenerFactory = EventListener.factory(EventListener.NONE);
// 设置代理
this.proxySelector = ProxySelector.getDefault();
// 设置cookie
this.cookieJar = CookieJar.NO_COOKIES;
// 设置创建Socket链接的Factory
this.socketFactory = SocketFactory.getDefault();
// 验证域名和证书的域名是否匹配
this.hostnameVerifier = OkHostnameVerifier.INSTANCE;
// 设置信任的证书,不信任之外的证书,默认是空的
this.certificatePinner = CertificatePinner.DEFAULT;
// 与身份认证有关。详见:https://square.github.io/okhttp/3.x/okhttp/okhttp3/Authenticator.html
this.proxyAuthenticator = Authenticator.NONE;
// 与身份认证有关。详见:https://square.github.io/okhttp/3.x/okhttp/okhttp3/Authenticator.html
this.authenticator = Authenticator.NONE;
// 连接池-缓存链接和清理空闲的连接
this.connectionPool = new ConnectionPool();
// 域名解析
this.dns = Dns.SYSTEM;
// 是否支持ssl重定向
this.followSslRedirects = true;
// 是否支持重定向
this.followRedirects = true;
// 失败后是否重试
this.retryOnConnectionFailure = true;
// 链接超时时间
this.connectTimeout = 10000;
// 读超时时间
this.readTimeout = 10000;
// 写超时时间
this.writeTimeout = 10000;
// ping的时间间隔。如果使用WebSocket请求设置pingInterval,以保活
this.pingInterval = 0;
}
Dispatcher-任务调度器:
public final class Dispatcher {
// 最大的异步请求数
private int maxRequests = 64;
// 单个host最大的请求数
private int maxRequestsPerHost = 5;
@Nullable
private Runnable idleCallback;
@Nullable
// 执行异步请求的线程池
private ExecutorService executorService;
// 尚未执行的请求
private final Deque readyAsyncCalls = new ArrayDeque();
// 正在执行的异步请求
private final Deque runningAsyncCalls = new ArrayDeque();
// 正在执行的同步请求
private final Deque runningSyncCalls = new ArrayDeque();
// 设置异步线程池
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
public synchronized ExecutorService executorService() {
if (this.executorService == null) {
// 默认的异步线程池
this.executorService = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return this.executorService;
}
// 异步任务,判断异步任务数和单个host的请求数
synchronized void enqueue(AsyncCall call) {
if (this.runningAsyncCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) {
this.runningAsyncCalls.add(call);
this.executorService().execute(call);
} else {
this.readyAsyncCalls.add(call);
}
}
}
ConnectionTool-缓存链接和清理空闲的连接:
public final class ConnectionPool {
// 负责清理空闲连接的线程池
private static final Executor executor;
// 最大空闲的连接数-默认值:5
private final int maxIdleConnections;
// 最大的空闲时间-默认值:5分钟
private final long keepAliveDurationNs;
// 负责清理空闲连接的Runnable
private final Runnable cleanupRunnable;
// 所有的连接
private final Deque connections;
final RouteDatabase routeDatabase;
boolean cleanupRunning;
public ConnectionPool() {
this(5, 5L, TimeUnit.MINUTES);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
////
}
static {
// 负责清理空闲连接的线程池
executor = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp ConnectionPool", true));
}
}
发送请求
常见的Okhttp配置
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) //设置连接超时
.readTimeout(60, TimeUnit.SECONDS) //设置读超时
.writeTimeout(60, TimeUnit.SECONDS) //设置写超时
.retryOnConnectionFailure(true) //是否自动重连
.build();
OkHttpClient 封装了HttpClient,性能类似,HttpClient可以配置更多内容,OkHttpClient使用更简单。