Okhttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由大名鼎鼎的Square公司贡献。
使用方式
引入库:
implementation 'com.squareup.okhttp3:okhttp:3.2.0'
发起get请求:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(20, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("")
.build();
try {
Response response = okHttpClient.newCall(request).execute();
response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
发起post请求:
RequestBody requestBody = new FormBody.Builder()
.add("","")
.build();
Request request = new Request.Builder()
.url("")
.addHeader("","")
.post(requestBody)
.build();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(20, TimeUnit.SECONDS)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
okhttp内部封装功能:
final Dispatcher dispatcher;
final Proxy proxy;
final List<Protocol> protocols;
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors;
final List<Interceptor> networkInterceptors;
final ProxySelector proxySelector;
final CookieJar cookieJar;
final Cache cache;
final InternalCache internalCache;
final SocketFactory socketFactory;
final SSLSocketFactory sslSocketFactory;
final TrustRootIndex trustRootIndex;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int connectTimeout;
final int readTimeout;
final int writeTimeout;
属性解释:
- Dispatcher作为调度器,内部有线程池,负责调度同步请求和异步请求队列
- Proxy 代理设置 相当于完全信任的中间代理
- List<Protocol> protocols 支持的具体http协议版本,有http/1.0,http/1.1等
- interceptors,networkInterceptors 这两个是okhttp中重要的拦截器
- Cache cache缓存的存储配置,默认没有。如果需要用,自己填写存储文件位置以及存储空间大小
- SocketFactory 使用默认的Socket工厂产生Socket,TCP传输层有关,三次握手
- SSLSocketFactory 带上ssl的socket的工厂,就是整个https连接的过程
- connectionPool 连接池
- dns 域名解析器,根据域名获得IP地址
- retryOnConnectionFailure 在请求失败后是否自动重试
- connectTimeout 连接超时时间
OkHttp流程图:
流程解析
创建OkhttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(20, TimeUnit.SECONDS)
.build();
官方建议使用单例创建OkHttpClient,即一个进程中只创建一次即可,以后的每次交易都使用该实例发送交易。这是因为OkHttpClient拥有自己的连接池和线程池,这些连接池和线程池可以重复使用,这样做利于减少延迟和节省内存,如果咱们每次发交易都创建一个OkHttpClient的话,将会浪费很多内存资源。
创建Call对象
val request = Request.Builder()
.url("")
.build()
okHttpClient.newCall(request)
一个Call对象表示一次请求,每一次请求都会生成一个新的Call,Call其实是一个接口,它的具体实现类是RealCall。
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}
RealCall构造方法:
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client;
this.originalRequest = originalRequest;
}
创建Call对象时候传进去了一个Request对象,Request对象表示用户的请求参数,并传入OkHttpClient对象。
Request类部分代码:
public final class Request {
private final HttpUrl url; //请求url
private final String method; //请求的get/post/put/delete方式
private final Headers headers; //请求头
private final RequestBody body; //请求体
private final Object tag;
private volatile URI javaNetUri; // Lazily initialized.
private volatile CacheControl cacheControl; // Lazily initialized.
private Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
}
执行请求
okhttp3中提供了两种请求方式:一种是同步请求,第二种是异步请求。同步请求调用call.execute()方法,异步请求调用call.enqueue(Callback callback)方法。
public interface Call {
/**立即调用请求,并阻塞,直到响应可以被处理或进入*/
Response execute() throws IOException;
/**
* 计划在将来某个时间执行的请求
*/
void enqueue(Callback responseCallback);
}
RealCall中实现:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
@Override public void enqueue(Callback responseCallback) {
enqueue(responseCallback, false);
}
void enqueue(Callback responseCallback, boolean forWebSocket) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
执行请求最终是通过Dispatcher类来调用,Dispatcher是okhttp3的任务调度核心类,负责管理同步和异步的请求,管理每一个请求任务的请求状态,并且其内部维护了一个线程池用于执行相应的请求。
Dispatcher类部分代码:
public final class Dispatcher {
/** 用于保存等待执行的异步队列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** 用于保存正在执行的异步队列 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
}
//调度器Dispatcher内部维护了一个ThreadPoolExecutor线程池,并直接将call对象传入线程池执行。
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
//同步执行
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
//异步执行
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以这么理解:把Dispatcher当成生产者,把线程池当成消费者,如果生产的线程小于可消费的范围,则立即加入消费队列;而当生产者生产的线程大于消费者所能承受的最大范围,就把未能及时执行的任务保存在readyAsyncCalls队列中,当时机成熟,也就是线程池有空余线程可以执行时,会调用promoteCall()这个方法把等待队列中的任务取出放到线程池中执行,并且把这个任务转移到runningAsyncCalls队列中去。
拦截器链
通过上面的分析咱们知道不管是同步请求还是异步请求,最后都会走getResponseWithInterceptorChain()方法,getResponseWithInterceptorChain()是okhttp3中的精髓设计之一。
Response response = getResponseWithInterceptorChain(forWebSocket);
具体实现过程:
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
}
class ApplicationInterceptorChain implements Interceptor.Chain {
private final int index;
private final Request request;
private final boolean forWebSocket;
ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {
this.index = index;
this.request = request;
this.forWebSocket = forWebSocket;
}
@Override public Connection connection() {
return null;
}
@Override public Request request() {
return request;
}
@Override public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
}
获取okhttpClient中设置的各个intercepter拦截器,通过拦截器链对请求数据和返回数据进行处理,内部采用责任链模式,将每一个拦截器对应负责的处理任务进行严格分配,最后将交易结果返回并回调到暴露给调用者的接口上。
参考文章
okhttp3源码分析:架构全面解析
网友评论