HttpClient关系图:
okhttp的基本使用:
//1.TODO: 创建HttpClient
HttpClient client = new HttpClient.Builder().build();
//2.TODO: 创建Request
Request request = new Request.Builder()
.url("http://www.baidu.com/")
.build();
//3.TODO: 执行请求
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, Throwable throwable) {
throwable.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) {
}
});
基本使用很简单,分三步:
1.通过HttpClient.Builder().build()
创建一个HttpClient
2.通过Request.Builder()
创建一个请求Request
3.通过httpClient.newCall
创建一个Call,并且把请求Request传入到call里面,然后去执行,并且拿到响应回调
源码分析:
一般的看源码都是从主流程看起,这里就从第一步看起
1.HttpClient.Builder().build()
从第一张关系图可以看出,HttpClient是控制整个流程的控制器,所以创建HttpClient是通过构建者模式builder来创建,可以配置一些信息
例如:设置超时时间,添加自己的拦截器,设置失败重连...等等
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.retryOnConnectionFailure(true);
2.创建请求,Request.Builder()
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tag = request.tag;
this.headers = request.headers.newBuilder();
}
Request的Builder可以配置请求路径url ,请求体body,请求头...
3.执行请求
Call call = client.newCall(request);
call.enqueue(new Callback() {...}
这里先是RealCall call = new RealCall(client, originalRequest, forWebSocket);
创建一个RealCall并且把OkHttpClient和Request保存到RealCall里面,然后是调用RealCall的enqueue去执行请求
@Override public void enqueue(Callback responseCallback) {
//TODO: 不能重复执行
synchronized (this) {
...
eventListener.callStart(this);
//TODO: 交给 dispatcher调度器,进行调度
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
这里会new AsyncCall(responseCallback)把Callback保存到一个AsyncCall对象里面
AsyncCall继承NamedRunnable,实现Runnable,所以AsyncCall虽然是叫call,但是和Callback没有关系,是Runnable的子类
然后会把AsyncCall交给dispatcher调度器去调度执行
这里到了本章重点,调度相关
先来看下调度器的源码
public final class Dispatcher {
private int maxRequests = 64;//TODO: 最大同时请求数
private int maxRequestsPerHost = 5; //TODO: 同时最大的相同Host的请求数
private @Nullable Runnable idleCallback;
//TODO: 线程池,维护执行请求和等待请求
private @Nullable ExecutorService executorService;
//TODO: 异步的等待队列,双端队列,支持首尾两端 双向开口可进可出
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//TODO: 异步的执行对列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//TODO: 同步的执行队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
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调度器里面主要是有这些东西:
maxRequests : //最大同时请求数
maxRequestsPerHost : //同时最大的相同Host的请求数
线程池ExecutorService: 负责异步去执行请求和等待请求
等待队列readyAsyncCalls : 没有执行的任务,添加到这里等待执行
执行队列runningAsyncCalls : 正在执行的异步任务队列
执行队列runningAsyncCalls : 正在执行的同步任务队列
线程池是一个核心数量为0,最大线程数为Integer.MAX_VALUE,非核心线程闲置60秒回收的线程池
再回头看client.dispatcher().enqueue(new AsyncCall(responseCallback))
做了什么?
dispatcher().enqueue()
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) <
maxRequestsPerHost) {
//TODO: 加入运行队列 并交给线程池执行
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
//TODO: 加入等候队列
readyAsyncCalls.add(call);
}
}
调度器会做一些判断
(runningAsyncCalls.size() < maxRequests
,如果同时进行的请求没有超过并发数 64,并且runningCallsForHost(call) < maxRequestsPerHost)
,同一个host正在运行的线程没有超过5条,就会加入到运行队列,并交给线程池执行,否则就加入等待队列readyAsyncCalls.add(call);
这里先看加入运行队列并且线程池去执行的流程,上面说到AsyncCall是Runnable的子类,所以当线程池去执行它的时候,会调用它的run()方法,而它的run()方法在NamedRunnable里面实现了,会调用execute()方法
@Override protected void execute() {
boolean signalledCallback = false;
try {
// TODO: 责任链模式,拦截器链 执行请求
//TODO: 拿到回调结果
Response response = getResponseWithInterceptorChain();
... //TODO: 删除部分代码
}
} finally {
//TODO: 移除队列
client.dispatcher().finished(this);
}
}
}
execute()里面会调用getResponseWithInterceptorChain()方法去连接网络,发起http请求并且拿到服务器响应结果,这里主要是分析调度器,先不看网络连接的实现
okhttp怎样执行等待队列里面的任务??
这里使用了一个巧妙的设计,使用try...finally来管理任务队列,最终都会进入finally里面,调用client.dispatcher().finished(this);
把执行完的任务从执行队列里面移除,并且执行等待队列里面的任务
void finished(AsyncCall call) {
//TODO: 传入执行队列, 执行的任务 call
finished(runningAsyncCalls, call, true);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
//TODO: 移除队列
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
//TODO: 检查执行 readyAsyncCalls 中的请求
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
//TODO: 如果线程闲置中, 调用run 执行起来
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
这里会调用!calls.remove(call)
把执行完的任务从执行队列里面移除,然后调用promoteCalls()
方法,检查执行等待队列readyAsyncCalls
里面的任务,并且判断是否需要唤醒闲置线程if (runningCallsCount == 0 && idleCallback != null)
调用idleCallback.run();
promoteCalls()检查执行等待队列
private void promoteCalls() {
//TODO: 检查 运行队列 与 等待队列
if (runningAsyncCalls.size() >= maxRequests) return;
if (readyAsyncCalls.isEmpty()) return;
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
//TODO: 相同host的请求没有达到最大
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
//TODO: : 加入执行队列,并且去执行
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return;
}
}
这里同样会先判断是否超过最大执行数量maxRequests
,并且相同host的请求没有达到最大,就会加入到执行队列,并且去执行,完成等待队列切换到执行队列的流程
整个调度的流程就完了
下一篇Okhttp主流程源码浅析(2),浅析责任链,对okhttp主流程深一步了解