OKHttp笔记本-分发流程

  1. 首先先下载一个源码,第三方框架源码下载下来比较方便

  1. 异步请求的分发流程

分发器如何决定将请求放入等待还是执行?

分发器移动到执行的条件?

分发器线程池的工作行为?

简单的使用方法

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.

}

}

  1. 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);

}

  1. 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);

}

  1. 请求流程

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 之间进行设置,根据实际情况进行调整。

需要注意的是,缓存任务大小的设置需要综合考虑以上几个方面,不同的应用可能需要不同大小的缓存任务。同时,可以通过定期清理或者自动清理机制来管理缓存任务,避免缓存占用过多存储空间和影响应用性能。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值