首先先下载一个源码,第三方框架源码下载下来比较方便
异步请求的分发流程
分发器如何决定将请求放入等待还是执行?
分发器移动到执行的条件?
分发器线程池的工作行为?
简单的使用方法
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求,可以省略
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: ");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//response.body().string() 获得服务器返回的数据
Log.d(TAG, "onResponse: " + response.body().string());
}
});
当OKHttp使用时会返回一个Call
Call call = okHttpClient.newCall(request);
源码在此,会返回一个call对象
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
request 是我们的请求体,RealCall持有了我们的请求体
client 是当前的门面 originalRequest是传入的请求体
RealCall implements Call 实现了 Call接口
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
//这个eventListener通过buildlener传入的
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
/**
* Configure a single client scoped listener that will receive all analytic events
* for this client.
*
* @see EventListener for semantics and restrictions on listener implementations.
*/
public Builder eventListener(EventListener eventListener) {
if (eventListener == null) throw new NullPointerException("eventListener == null");
this.eventListenerFactory = EventListener.factory(eventListener);
return this;
}
在得到call对象之后调用了enqueue方法
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
//前面realCall创建的listener
eventListener.callStart(this);
//获取Client的分发器然后分发 client是门面的对象
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
//这个方法可以看到主要是为retryAndFollowUpInterceptor增加了一个callStackTrace,主要是用来追踪堆栈的信息
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
在Dispatcher
分发器,对于异步任务有两个队列
一个是执行队列,一个是等待队列
在enqueue 决定放在哪个队列
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
//正在执行的数量小于64 考虑对自己客户端的压力
//相同的请求数不能大于5个 考虑的是服务器的压力
//不能太小也不能太大,如果太小资源消耗太大,太大对服务器客户端压力太大
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
//然后把任务交给线程池执行
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
两个队列数据结构
为什么使用双端对列?可不可以使用其他的对列,当前版本暂时没有使用双端的地方
/** 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<>();
//发起的相同请求数
/** 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;
}
linux最多打开100个文件
Socket 文件句柄
IO文件句柄
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;
}
接下来
final class AsyncCall extends NamedRunnable {
public abstract class NamedRunnable implements Runnable {
//执行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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
接下来调用了分发器的finish this是 AsyncCall
client.dispatcher().finished(this);
}
}
}
// synchronized (this)维护了数据的准确性防止并发
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
//首先移除掉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的线程池
public synchronized ExecutorService executorService() {
if (executorService == null) {
核心线程数0,最大线程数Max,闲置时间60s,对列是SynchronousQueue,拒绝策略
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
这是最大并发线程池的吞吐量
SynchronousQueue特点:=没有容量的容器,只要进入就会发起一个新线程,所以OKHTTP内使用该容器进行任务
ArrayBlockingQueue特点:创建时候需要给一个容量,表示存的数据大小,但是有可能顺序会颠倒,如果不给当前容量会一直添加,导致一直卡住
为什么使用线程池,线程池会管理当前的线程
可以缓存60s,帮我们管理线程
线程池提交的方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//当前工作小于核心线程数 OKHttp的设置是0所以没有这个判断
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//会进入当前对列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
//ArrayBlockingQueue 的执行现象
//当前第一个任务进入就会创建一个线程跑这个任务
//但是如果任务一如果一直不结束,任务二虽然进入了对列,但是一直不执行
//如果此时提交任务三,会判断是否到达最大线程数,当前是max,所以会开启新线程跑3任务,执行完后从对列中取出二
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
//addWorker会new一个Thread去跑当前的任务
}
else if (!addWorker(command, false))
reject(command);
}
OKHTTP的同步请求
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//调用同步的分发,直接发起请求(getResponseWithInterceptorChain)
//同步请求直接将当前的保存在请求的对列里
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);
}
}
同步请求的executed方法
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
请求流程
getResponseWithInterceptorChain
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 (!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());
return chain.proceed(originalRequest);
}
webSocket http ftp rtmp。。。协议
OKhttp的责任链模式
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection 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.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != 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, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//取出第0个责任
Interceptor interceptor = interceptors.get(index);
//然后执行它的intercept
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
会将链的新对象传递给责任员,每一次都是新对象、
疑问?
为什么不使用单例进行管理而是用每一次都新对象方式?是不是链上任务执行完就可以进行销毁
OkHttp 的责任链不使用单例管理,而是每次都使用新对象,是为了保证线程安全和避免状态混乱的问题。
OkHttp 的责任链是由多个拦截器(Interceptor)组成的,每个拦截器都负责处理一部分网络请求的任务,并将请求传递给下一个拦截器。如果使用单例管理,那么所有拦截器都会共享同一个实例,可能会导致线程安全和状态混乱的问题。
例如,如果一个拦截器在处理请求时修改了请求的状态,然后将请求传递给下一个拦截器,如果下一个拦截器也修改了请求的状态,那么就会导致状态混乱的问题。如果每次都使用新对象,那么每个拦截器都有自己独立的状态,不会相互干扰,可以保证线程安全和状态的一致性。
另外,OkHttp 的责任链还涉及到线程池的使用,每个拦截器都可能会开启新的线程来处理网络请求的任务,如果使用单例管理,可能会导致线程安全和性能问题。如果每次都使用新对象,那么每个拦截器都有自己独立的线程池,不会相互干扰,可以保证线程安全和性能的一致性。
综上所述,OkHttp 的责任链不使用单例管理,而是每次都使用新对象,是为了保证线程安全和避免状态混乱的问题。同时,这也符合 OkHttp 的设计哲学,即简单、灵活和可定制化。
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Response response = interceptor.intercept(next);
保持当前所有的参数
责任员做完任务后悔继续执行链对象的分发方法
Response networkResponse = chain.proceed(requestBuilder.build());
链上的任务出现异常可以通过try crash进行保护
View的事件分发也是责任链..
OKhttp的缓存应该涉及几个方面,都参考什么?
OkHttp 缓存的任务大小实际应用是根据网络请求的资源类型、网络环境和设备存储空间等因素来确定的。一般来说,缓存任务大小的设置应该综合考虑以下几个方面:
1. 资源类型:对于一些静态资源,如图片、音视频等,可以设置较大的缓存任务大小,以减少服务器的请求次数和用户的等待时间。而对于一些动态资源,如 JSON 数据等,由于其实时性较强,可以设置较小的缓存任务大小,以避免数据过期和不一致的问题。
2. 网络环境:在网络环境较好的情况下,可以设置较小的缓存任务大小,以避免占用过多的存储空间和影响网络性能。而在网络环境较差的情况下,可以适当增加缓存任务大小,以减少请求次数和用户等待时间。
3. 设备存储空间:缓存任务大小还需要考虑设备存储空间的限制,以避免占用过多的存储空间和影响应用性能。一般来说,缓存任务大小应该在 50MB ~ 200MB 之间进行设置,根据实际情况进行调整。
需要注意的是,缓存任务大小的设置需要综合考虑以上几个方面,不同的应用可能需要不同大小的缓存任务。同时,可以通过定期清理或者自动清理机制来管理缓存任务,避免缓存占用过多存储空间和影响应用性能。