okhttp是一个开源的网络请求框架,由square公司发布,目前比较流行的版本是okHttp3。
okhttp3框架源码地址: https://github.com/square/okhttp
本文只对okHttp3做大致讲解,不具体分析源码细节等。
就从如何使用开始讲起。
okHttp3简单的使用步骤如下:
1.得到OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient.Builder()
//建造者模式,可选择添加各种拦截器等
.xxx()
.xxx()
....
.build();
2.得到Request对象
Request request = new Request.Builder()
.url(请求地址)
.build();
3.发起请求
//同步请求
okHttpClient.newCall(request).execute();
//异步请求
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
//正确响应
}
@Override
public void onFailure(Call call, IOException e) {
//请求失败
}
});
看它的处理逻辑:
第一步第二步,使用建造者模式得到OkHttpClient对象和Request对象,这里不细讲了。
第三步开始发起请求,下面是OkHttpClient的newCall()方法:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
得到了个RealCall对象
return new RealCall(this, request);
}
//RealCall的构造方法
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client;
this.originalRequest = originalRequest;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
}
得到了RealCall对象后,具体发起网络请求时,分为同步和异步请求。
一、异步请求
发起异步请求时,调用的是RealCall的enqueue()方法。
//RealCall的enqueue()方法
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
这里client.dispatcher()得到的是建造者模式生成OkHttpClient对象的时候,new的一个Dispatcher对象。
Dispatcher是负责请求管理与调度的,维护了一个正在运行的请求队列,待完成的请求队列等。
public final class Dispatcher {
private int maxRequests = 64;//最大并发请求数
private int maxRequestsPerHost = 5;//每个主机最大请求数为5
private Runnable idleCallback;
/** 线程池 */
private ExecutorService executorService;
/** 待运行的异步请求队列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** 正在运行的异步请求队列,包括已取消的 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** 正在运行的同步请求队列 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
//创建单例的线程池
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;
}
看下Dispatcher的enqueue()方法:
synchronized void enqueue(AsyncCall call) {
//如果正在请求的数量未达到最大数量,就加入运行队列并运行
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
//如果正在请求的数量已达到最大数量,就加入等待队列,等待取出
readyAsyncCalls.add(call);
}
}
异步请求是在加入队列的时候,被包装成AsyncCall对象的。由Dispatcher的enqueue()可见,AsyncCall是交给线程池来执行了。
AsyncCall是Runnable的子类,发起请求最终会执行它的execute()方法。
追代码的话,流程为ThreadPoolExecutor的execute() -> addWorker() -> addWorker() 里的 t.start() ->AsyncCall.run()->AsyncCall.execute()。
//AsyncCall的execute()方法
@Override 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 (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
异步请求的流程分析到这里先放一边,来看看同步请求的处理。
二·、同步请求
发起同步请求时,调用的是RealCall的execute()方法:
@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();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
三、处理网络请求的流程
同步与异步都是用getResponseWithInterceptorChain()这个方法来真正处理网络请求的流程。
下面就看看getResponseWithInterceptorChain()这个方法做了什么:
private Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> 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 (!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意为拦截器,是OKHTTP中提供的一个接口:
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
上面getResponseWithInterceptorChain()这个方法做了这样的处理:
1.把okHttpClient的各种拦截器添加到拦截器列表interceptors中。
2.拦截器列表作为构造参数,生成RealInterceptorChain对象。
3.通过RealInterceptorChain 的proceed(originalRequest)得到请求结果。
第一步添加到拦截器列表interceptors中的各种拦截器,包括下面这些:
1)在配置 OkHttpClient 时设置的 interceptors;
2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
4)负责读取缓存直接返回、更新缓存的 CacheInterceptor;
5)负责和服务器建立连接的ConnectInterceptor;
6)配置 OkHttpClient 时设置的 networkInterceptors;
7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。OkHttp的这种拦截器链采用的是责任链模式,这样的好处是将请求的发送和处理分开,并且可以动态添加中间的处理方实现对请求的处理、短路等操作。
原文链接:https://blog.csdn.net/import_sadaharu/article/details/81416244
下面是RealInterceptorChain 的proceed方法:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpStream, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpStream != 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 the only call to chain.proceed().
if (this.httpStream != 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, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
xxx其它处理,略过
return response;
}
其中proceed()方法里的这几句,要单独拿出来讲下:
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
这里的拦截器链,实现了一个递归的效果。
默认情况下,这里的interceptors.get(index),会得到RetryAndFollowUpInterceptor。
在RetryAndFollowUpInterceptor的intercept方法里,进行了网络通信相关的判断和拦截,如果不满足拦截条件的话,就会调用chain.proceed()方法。
这样,流程又走到了这里。而且由于上次传入的是index+1,所以这次interceptors.get(index)得到的是interceptors列表的下一个拦截器了,也就是BridgeInterceptor。
同理,BridgeInterceptor的intercept方法里,进行一些特有的判断和拦截,如果不满足拦截条件的话,就会调用chain.proceed()方法。
值得一提的是CacheInterceptor,缓存拦截器。
缓存拦截器中的缓存策略:
(1)首先会尝试从缓存Cache中获取当前请求request的缓存response,并根据传入的请求request和获取的缓存response通过缓存策略对象CacheStrategy的工厂类的get方法生成缓存策略类CacheStrategy。
(2)CacheStrategy的工厂类的get方法里面会根据一些规则生成CacheStrategy,这里面的规则实际上控制着缓存拦截器CacheInterceptor的处理逻辑。而这些规则都是根据请求的Request和缓存的Response的header头部信息来生成的,比如是否有noCache标志位,是否是immutable不可变的,以及缓存是否过期等。
(3)CacheInterceptor会根据CacheStrategy中的networkRequest和cacheResponse是否为空,来判断是请求网络还是直接使用缓存。
(4)网络得到的networkResponse会和本地已经存在的cacheResponse,做比较,决定是否来更新缓存的cacheResponse。作者:xxq2dream 链接:https://www.jianshu.com/p/d8590c4232a3
即如果满足一定要求的话,就会从缓存中取响应并返回,而不会发起网络请求了;否则传递给下一个拦截器。
依次类推,最终的网络请求,是被CallServerInterceptor处理的。CallServerInterceptor是拦截器链中最后一个拦截器,负责将网络请求提交给服务器。
CallServerInterceptor的intercept方法里发起请求并得到响应后,这个响应Response会按照原先递归调用的相反顺序层层传递回去,最后也就成为RealCall里的getResponseWithInterceptorChain()的结果,这个结果会传入Callback里,由用户自己处理。
总结
(1)拦截器链和缓存策略都是OkHttp3的亮点所在,此外还有复用连接池等。
(2)拦截器链通过责任链的模式,将网络请求过程中的职责功能都分割开,分别用不同的拦截器来完成失败重连、缓存处理、网络连接等问题。而且用户还可以添加自定义的拦截器,非常灵活,满足面向对象的开闭原则。
(3)缓存策略指的是对于请求的响应的缓存。OkHttp中有专门类Cache来实现缓存,Cache中采用了DiskLruCache,以Request的URL的md5为key,相应Response为value。此外Cache中还通过外观模式对外提供了InternalCache接口变量,用于调用Cache中的方法,也满足面向对象的接口隔离原则和依赖倒置原则等。
(4)缓存拦截器中会根据生成的缓存策略类CacheStrategy的2个变量networkRequest和cacheResponse来决定是连接网络还是直接使用缓存。如果2个变量都为空,则直接返回504,请求失败;如果缓存不为空,则直接返回缓存;如果networkRequest不为空,就通过调用RealInterceptorChain的proceed方法将请求继续转发到下一个拦截器。作者:xxq2dream 链接:https://www.jianshu.com/p/d8590c4232a3