一、Okhttp同步和异步执行流程
OKHttp如何进行同步请求和异步请求?这一切都要从两个官方实例说起:
初始化OkhttpClent
OkHttpClient client = new OkHttpClient.Builder()
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.build();
异步请求:
final Request request = new Request.Builder()
.url(url)
.get()
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("OkHttpActivity",response.body().string());
}
});
同步请求:
final Request request = new Request.Builder()
.url(url)
.get()
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
小节:
这两种方式都是通过接口Call的具体实现类RealCall来实现的,同一个RealCall只能被执行一次接口请求。
其中同步请求是在当前调用的线程执行,多个同步请求则通过Dispatcher 维护的一个Deque队列,来
保证RealCall的每一次执行顺序。
而异步请求则是在Okhttp对应的线程池中执行,多个异步请求则通过Dispatcher维护的两个Deque队列,来
处理AsyncCall
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
二、从源码的角度分析异步请求:
// 异步请求 mCall 其实是接口Call的实现类 RealCall
mCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("OkHttpActivity", response.body().string());
}
});
RealCall的enqueu()方法
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
// executed 全局变量 只被赋值一次,也就是说一个RealCall对象只能被执行一次!!
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// 捕获调用堆栈跟踪
captureCallStackTrace();
// OKHttp 的事件流程监听
eventListener.callStart(this);
// 这句是核心逻辑,其实通过OkhttpClient获得 Dispatcher,并调用enqueue方法
// 将RunAble 的"实现类" AsyncCall 添加 相应的队列中。
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Dispatcher 的enqueue()
synchronized void enqueue(AsyncCall call) {
// 这两个判断添加很重要 面试被问过
// 1、正在运行的异步请求数量 不能超过64 2、当前连接的Host不能超过5个
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
// 将 Runable 添加到正在运行队列中
runningAsyncCalls.add(call);
// 开启线程池 执行Runable
executorService().execute(call);
} else {
// 将Runable 添加到 等待线程
readyAsyncCalls.add(call);
}
}
这里:通过maxRequests的限制,避免了请求过多时,手机开启过多的线程;通过maxRequestsPerHost的限制,减少在一个时间点对同一服务器的访问次数,减少服务器的压力。
由于AsyncCall 是接口Runable的实现类,肯定会实现RunAble的Run()方法。
// AsyncCall 继承
final class AsyncCall extends NamedRunnable
// 而 NamedRunnable 实现 Runnable
public abstract class NamedRunnable implements Runnable
NamedRunnable 主要实现了RunAble的Run方法。
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
// RunAble在线程池中执行时调用
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
// 由子类AsyncCall 实现
protected abstract void execute();
}
AsyncCall 的execute()
@Override protected void execute() {
boolean signalledCallback = false;
try {
// 这句代码先不要管,
// 只需知道:通过getResponseWithInterceptorChain()方法,能获得请求响应Response
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 {
eventListener.callFailed(RealCall.this, e);
// 接口请求 失败回调
responseCallback.onFailure(RealCall.this, e);
}
} finally {
// AsyncCall 执行完毕,调用finished()
client.dispatcher().finished(this);
}
}
Dispatcher 的 finished 主要是 1、移除执行完毕的AsyncCall 2、获得下一个 线程池执行
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
// runningAsyncCalls 从运行中的CAll中移除
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
// 从等待队列中 获取下一个 继续执行
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<AsyncCall> 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.
}
}
小节:
①、OkHttp异步请求,首先创建接口RunAble的实现类AsyncCall,并将其添加到正在运行的队列或者等待队列中,
②、之后线程池执行AsyncCall(),
③、在AsyncCall() 的 execute()方法中 Response response = getResponseWithInterceptorChain(); 获得网络请求响应,并且通过Callback回调给用户
④、 最后通过 client.dispatcher().finished(this); 从队列中清除执行完毕的AsyncCall ,并且继续下次请求。
至此 OkHttp的异步请求流程梳理完成,下面的这张流程图更加清晰明确OkHttp的异步请求流程
三、从源码的角度分析同步请求:
相较Okhttp的异步请求,同步请求的流程比较简单点。
// 执行同步请求
Response response = mCall.execute();
依旧是接口Call的实现类RealCall
@Override public Response execute() throws IOException {
synchronized (this) {
// 每个RealCall 这能只执行一次
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
// 调用Dispatcher的executed 其实就是将RealCall
// 添加到同步队列Deque<RealCall> runningSyncCalls = new ArrayDeque<>();之中
client.dispatcher().executed(this);
// 暂时不做分析,只需明白调用 getResponseWithInterceptorChain() 获得网络请求响应Response
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
// 同步请求 执行完成
// 将RealCall从 runningSyncCalls 队列中移除
client.dispatcher().finished(this);
}
}
向Dispatcher的 Deque runningSyncCalls = new ArrayDeque<>() 添加RealCall
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
向Dispatcher的 Deque runningSyncCalls = new ArrayDeque<>() 移除 执行完毕的 RealCall
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
小节:
Okhttp同步请求,所谓的同步请求就是在当前线程进行网络请求,直到网络请求成功或者失败后才能继续执行后续的代码。
至此 OkHttp的同步请求流程梳理完成,下面的这张流程图更加清晰明确OkHttp的同步请求流程
参考文章链接: