OKHttp系列六、OKHttp的核心Dispatcher源码与运行逻辑解析

1、同步请求的用法:

Request mRequest = new Request.Builder().url("www.baidu.com").get().build(); 
Call call = client.newCall(mRequest); 
Response  response = call.execute();

上面Call是一个接口,execute()是一个抽象的方法,由RealCall.java具体实现该方法,它默认运行在主线程里面,RealCall中execute()源码如下:

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);
	}
}

我们注意到上面添加注释的一行代码:client.dispatcher().executed(this)——他是Dispatcher里面的一个方法,client.dispatcher()实际上就是一个Dispatcher对象,里面非常简单,就是将一个RealCall添加到正在运行的队列中去:

synchronized void executed(RealCall call) {
	runningSyncCalls.add(call);
}

runningSyncCalls——就是我们前面提到的 正在执行、还没有执行完成以及取消的同步请求队列
每当我们发出一个同步请求时,Dispatcher就直接将他加入到执行队列里面去并开始执行,他并不需要像异步请求那样考虑各种场景和影响因素。

2.异步请求的用法:

Request mRequest = new Request.Builder().url("www.baidu.com").get().build(); 
Call call = client.newCall(mRequest);
call.enqueue(new Callback() {
	@Override
	public void onFailure(Call call, IOException e) {
	}
	@Override
	public void onResponse(Call call, Response response) throws IOException {
	}
});

上面加粗的Response部分就是我们网络请求到的结果。同样的,上面Call是一个接口,enqueue ()是一个抽象的方法,由RealCall.java具体实现该方法,它默认运行在子线程里面,但是经过Dispatcher内部Handler的调度又将运行线程切换回了主线程。RealCall中enqueue ()源码如下:

public void enqueue(Callback responseCallback) {
	synchronized (this) {
		if (executed) {
			throw new IllegalStateException("Already Executed");
		}
		executed = true;
	}
	captureCallStackTrace();
	eventListener.callStart(this);
	client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

我们注意到上面加粗的一行代码:

——client.dispatcher().enqueue(new AsyncCall(responseCallback)),client.dispatcher()实际上就是一个Dispatcher对象;

——enqueue(new AsyncCall(responseCallback))方法是Dispatcher里面的一个方法, AsyncCall是RealCall.java里面的一个内部类,他是NamedRunnable的子类,NamedRunnable又实现了Runnable,所以AsyncCall的本质上就是一个Runnable接口,具体的请求业务就在Runnable接口的run方法里面实现。

——responseCallback是回调接口Callback的对象,用于对请求结果的处理。
在Dispatcher中enqueue()的源码如下:

synchronized void enqueue(AsyncCall call) {
	if (runningAsyncCalls.size()<maxRequests && runningCallsForHost(call)<maxRequestsPerHost) {
		runningAsyncCalls.add(call);
		executorService().execute(call);
	} else {
		readyAsyncCalls.add(call);
	}
}

If判断的意思是:当异步请求的队列长度小于允许的最大并发请求数量64并且主机允许的最大并发请求数量小于5的时候,就将该请求加入到正在执行、还没有执行完成以及取消的同步请求队列里面去,并立即开始执行请求操作。否则,只是将该请求加入到准备就绪将要执行的异步请求队列里面去等待执行。

扫描二维码关注公众号,回复: 11152255 查看本文章

executorService()就是获取到当前线程池线程池,通过execute方法执行请求任务。源码如下:

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;
}

上面ThreadPoolExecutor构造方法里面需要注意一点:Integer.MAX_VALUE:这里将线程池的最大数量设置为无限大,实质上是无法实现的,因为他同时受maxRequests=64和maxRequestsPerHost=5两个变量限制的,上面的if判断语句中,比较大小的时候,用的是“<”符号,因此线程池这里实际上最大能够实现的线程数量是63×4=252个。

——有个问题:call执行完成后,需要在runningAsyncCalls队列中移除这个线程,那么队列readyAsyncCalls中的线程在哪里执行呢?什么时候才会执行呢?
首先,execute方法的具体执行是在RealCall.java的内部类AsnycCall.java里面实现的。

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 {
			eventListener.callFailed(RealCall.this, e);
			responseCallback.onFailure(RealCall.this, e);
		}
	} finally {
		client.dispatcher().finished(this);
	}
}

这里,try里面的代码很好理解,就是如果发现拦截器里面的isCanceled方法为true,就表示请求任务被取消,那么调请求失败的方法,异常标记flag=“Canceled”;否则调用onResponse方法返回请求结果。Catch方法都是对失败的处理。最关键的一个:finally语句块里面的代码:

——client.dispatcher().finished(this)——client.dispatcher()实质上就是一个Dispatcher对象。finished方法裏面的逻辑才是我们这里最关心的问题:

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) {
		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();
	}
}

上面代码中,第一个if判断好理解,抛出无法移除已经执行完毕的请求移除;最后一个if判断也好理解,开始执行新的请求逻辑;第二if逻辑稍微复杂点,主要作用是调整我们的异步队列数据的:
——上面加粗的部分代码,调用finished方法时,传入的是true;因此后面第二if判断里面的逻辑(也就是加粗斜体部分代码的逻辑)一定会被执行,promoteCalls()方法源码如下:

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.
	}
}

/** Returns the number of running calls that share a host with {@code call}. */
private int runningCallsForHost(AsyncCall call) {
	int result = 0;
	for (AsyncCall c : runningAsyncCalls) {
		if (c.get().forWebSocket) continue;
		if (c.host().equals(call.host())) result++;
	}
	return result;
}

promoteCalls方法里面加粗的部分为关键代码,通过循环遍历已加入准备就绪将要执行的异步请求队列,获取队列里面的第一个对象,然后从readyAsyncCalls里面移除(i.remove()代码)并加入到队列runningAsyncCalls里面去(runningAsyncCalls.add(call)代码)。并同时调用线程池的execute方法开始执行请求任务(executorService().execute(call)代码)。至此,前面的问题得到了圆满的回答。

原创文章 118 获赞 149 访问量 9万+

猜你喜欢

转载自blog.csdn.net/haoyuegongzi/article/details/103443914