目录
一 、简介及框架
OkHttp是安卓端最火热的轻量级网络框架,由移动支付Square公司开源,用于替代HttpUrlConnection和Apache的HttpClient,事实上,Android4.4开始,google已经开始将源码中的HttpURLConnection替换为OkHttp,Android6.0里已移除HttpClient。由于该框架功能强大,而应用非常简单,越来越多的安卓开发者应用了该框架。
具有如下的功能和特性:
1、支持同步和异步请求,支持get和post请求,支持上传和下载文件,加载图片;
2、支持Https,Http 1.0 1.1 2.0, websocket, SPDY等协议,3.7版本开始剥离SPDY,转而大力支持 http2.0
3、支持Http缓存,避免重复请求;
4、内部维护连接池,支持多路复用,减少连接创建开销;
5、无缝支持Gzip压缩,减少数据流量;
6、内部维护任务队列线程池,友好支持并发访问,最大并发64,单个host最大5个;
7、socket创建支持最佳路由;
8、服务器配置多IP情况下,当前IP请求失败,支持自动切换到其他IP;
9、使用Okio来简化书看的访问和存储,提高性能;
10、OkHttp还处理了代理服务器问题和SSL握手失败问题。
上图是OkHttp的总体架构,大致可以分为以下几层:
Interface——接口层:接受网络访问请求
Protocol——协议层:处理协议逻辑
Connection——连接层:管理网络连接,发送新的请求,接收服务器访问
Cache——缓存层:管理本地缓存
I/O——I/O层:实际数据读写实现
Inteceptor——拦截器层:拦截网络访问,插入拦截逻辑
二 、使用方法
定义网络管理单例,全局只声明一次OkHttpClient 。
private OkHttpClient mClient;
/**
* 异步请求
*
* @param request
* @param callback
*/
public void sendAsyncRequest(Request request, Callback callback) {
mClient.newCall(request).enqueue(callback);
}
/**
* 同步请求
* @param request
* @return
*/
public Response sendSyncRequest(Request request) {
Response response = null;
try {
response = mClient.newCall(request).execute();
} catch (Exception e) {
}
return response;
}
业务层要发请求调用的示例:
//构建请求对象
Request request = new Request.Builder().url("https://baidu.com").build();
//异步请求
NetManager.getInstance().sendAsyncRequest(request, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
//同步请求
Response response = NetManager.getInstance().sendSyncRequest(request);
三、调用流程分析
OkHttp由OkHttpClien对外提供统一接口,支持同步和异步请求,无论哪种请求,都会封装为RealCall对象,再调用RealCall的同步或异步方法,并最终都会调用getResponseWithInterceptorChain执行请求任务,不同的同步的请求会直接调用该方法,而异步请求会再封装为AsynCall,再将其加入Dispatcher的任务队列,Dispatcher则维护任务调度线程池,对异步请求任务进行分发,当任务执行时会在新线程里调用RealCall的getResponseWithInterceptorChain.具体调度流程如下图所示:
同步流程:
OkHttpClient:
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
RealCall:
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//加入同步请求队列
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 {
//从同步队列移除
client.dispatcher().finished(this);
}
}
Dispatcher:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
异步请求流程:
OkHttpClient:
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
RealCall:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//先构造AsyncCall对象,加入分发器的异步任务队列
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Dispatcher:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//并发请求数在64个内,且单host并发数5个内,加入异步执行队列,放入线程池
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
//已达上限则加入等待队列
readyAsyncCalls.add(call);
}
}
无论是同步还是异步请求,完成后都会调用Dispatcher的finish,从队列移除:
Dispatcher:
private void finished(Deque calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
//异步请求调finish后需要执行promoteCalls,优化调度器,处理等待队列中的任务
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
//没有在执行的任务,包括同步和异步,则回调空闲通知线程(自定义)
idleCallback.run();
}
}
/*
* 优化调度器,执行等待队列中的任务,直至并发达上限
*/
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
从上面流程可见,真正处理请求的核心方法是getResponseWithInterceptorChain,该方法主要处理一系列的Interceptor,
代码如下:
RealCall:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
//添加一系列的拦截器到列表
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 (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
//构造拦截链
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//调用proceed按顺序执行每个拦截器
return chain.proceed(originalRequest);
}
}
拦截器处理的流程如下图所示:
RetryAndFollowUpInterceptor:在网络请求失败后进行重试,当服务器返回重定向时直接发起新的请求,并在允许情况下复用当前连接。
BridgeInterceptor:处理头部协议内容,包括设置请求内容长度,编码格式,gzip压缩,并对响应内容进行解压,添加cookie,还有User-Agent,Host,Keep-Alive等。
CacheInterceptor:负责缓存的管理,如果有符合条件的缓存则直接返回,当服务器返回有改变则更新当前缓存,缓存失效则删除。
ConnectionInterceptor:从连接池中查找合适的连接,如果有合适的则复用已有的连接,没有则新建连接。
CallServerInterceptor:负责向服务器发起真正的请求,并读取返回服务器的响应。
四、拦截器原理
OkHttp的Interceptor是一种典型的责任链模式,将每个处理单独包装为一个个独立的Interceptor,一个完整的请求由执行一组Interceptor来完成。下面分析下Interceptor的具体原理,核心代码如下:
Interceptor:
public interface Interceptor {
/*
* 拦截器要处理的逻辑
* 除了最后一个拦截器,其他的都会调用chain.proceed,继续处理下一个拦截器
* 等待下一个拦截器返回Response
*/
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
/*
* 从下一个拦截器开始构造链,然后执行当前拦截器
*/
Response proceed(Request request) throws IOException;
...
}
以RetryAndFollowUpInterceptor为例,intercept的处理流程是:
RetryAndFollowUpInterceptor:
@Override public Response intercept(Chain chain) throws IOException {
...
try {
//处理下一个拦截器,等待返回response
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
}
...
}
chain.proceed的核心逻辑如下:
RealInterceptorChain:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
{
...
//从下一个拦截器开始构造链
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//执行当前拦截器
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
...
}
拦截器链的执行流程如图:
五、总结
本文介绍了okhttp具有的功能特性,简单调用方法,包括异步和同步请求,分析了总体的框架层次,然后从源码角度分析了异步和同步请求任务的调度流程,执行的流程会通过若干个拦截器完成请求各个阶段的逻辑处理,说明了各个拦截器的功能,最后分析了拦截器的源码及原理,通过本文能对okhttp有整体的了解。