OkHttp 3.x 源码解析之Dispatcher
来源:互联网 发布:电脑软件服务打不开 编辑:程序博客网 时间:2024/06/10 07:08
tamic: / www.tamicer.com, 公众号:开发者技术前线
Dispatcher概念
Dispatcher中文是分发器的意思,和拦截器不同的是分发器不做Action事件处理,只做事件流向。在Okhttp中Dispatcher
负责将每一次Requst进行分发,压栈到自己的线程池,并通过调用者自己不同的方式进行异步和同步处理!
流程来向
接着上一章节我提到的拦截器概念,说明了okHttp每一次发起请求之前先进行Resqust构造,然后通过OkhttpClent进行构造真实的调用对象Call,接着执行者(Call )调用
enqueue()发起请求,下面再重复上一次代码!看着代码自己体会!
Request request = new Request.Builder() .get() .url(baseUrl) .build(); //Client OkHttpClient client = new OkHttpClient(); //创建的是一个RealCall对象 final Call call = client.newCall(request); //异步调用 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.d("tamic", response.body().string()); } }); }
那么这个Dispatcher
和我们的以上说的这几个类Request
,OkHttpClient
,Call
有毛关系?
别急…..听我讲完 , goto!!!!
我们通过主动 通过关键字new
出OkHttpClient
的实例构造出一个Call
,而这个call通过自己的实现类,调用call.enqueue()
进行发起调用,就是这么一个call中实际上就包含了一个Client的引用,而这个引用包含一个 Dispatcher
成员变量,好了这个时候你已经明白了这东西怎么和ohttpclient
勾搭上关系的!
final class RealCall implements Call { final OkHttpClient client; final Request originalRequest; final boolean forWebSocket; // Guarded by this. private boolean executed; RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { final EventListener.Factory eventListenerFactory = client.eventListenerFactory(); this.client = client; //.... } @Override public Request request() { return originalRequest; } @Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); // so! 看这里,这里通过你自己的构造的client,继续传给Call,而call的实现类RealCall则保持了这个dispather. client.dispatcher().enqueue(new AsyncCall(responseCallback));//AsyncCall实际上就是线程的子类 }
概念说了,代码上了,那么来个图,可好?
上面解释完dispacther走向的流程,下面就来说说Dispatcher的工作原理。
Dispatcher原理
Dispatcher内部维护者一套线程队列,包含三大点,一个等待线程队列(readyAsyncCalls)
,一个正在运行的同步队列(runningAsyncCalls ),正在运行的异步队列(runningSyncCalls),当然有线程,必须有一个池的概念 ExecutorService
就是这里的一个管家角色,在具体了解之前,不妨先看看源码。
public final class Dispatcher { private int maxRequests = 64; private int maxRequestsPerHost = 5; private @Nullable Runnable idleCallback; /** Executes calls. Created lazily. */ private @Nullable ExecutorService executorService; /** 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<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); public Dispatcher(ExecutorService executorService) { this.executorService = executorService; }
最大请求数支持64个。最大的预备主机支持5个。并且Dispatcher 已被Google小姐姐设置为Final的,就是让大家不要篡改这个调度机制。继续看看提供了那些方法;
一 入队
上面提到当调用call.enqueue()
的时候,Reall这个调用者会调用: client.dispatcher().enqueue(new AsyncCall(responseCallback))
这个函数,那么这里你也猜到我就瞎编神马了,好,就来大话下这个 enqueue()
! 上源码:
synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); }}
明白?一个同步锁决定了Dispatcher 全局只有一个并支持同步了,因为我们调用了异步则要保证这段代码必须要被锁定执行,回调到对应 callBack
中。纳尼?异步调用怎么到了分发器就是同步了?
你tm这就就骗我们小学生,好好 我给你看个东西。接下来我让你明白这玩意好好不用
上面的方法将我们生产的Reall产生的线程add()
到执行队列中,并通过我们的管理器ExecutorService
执行这个runnbale
, 玩过多线程同学突然明白了,咋就这么简单了,这样的线程池我分分钟给你干几个,但是我真没想能这么用。
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;}
二 出队:
有add肯定有remove函数,接着看看cannel函数,然而你要明白dipath的取消并不是由他自己完成,而是交给了拦截器( RetryAndFollowUpInterceptor
)处理,对于发出去的请求无论等待还是以只执行完成,都需要进过拦截器处理,那么拦截器将会标记,如果为取消,则中断callback流程,
代码如下:
public void cancel() { canceled = true; StreamAllocation streamAllocation = this.streamAllocation; if (streamAllocation != null) streamAllocation.cancel();//这里不再具追究,内部则调用coneation.canel()将连接中断}
上面有一点还未解释,
为什么call.cancel()取消为什么会调用拦截器的cancel()?
这是因为Dispatcher
的取消函数会调用这个穿进来的Call,而这个call实际上就是我们自己在调用Call.equen()是构造的那个,开发者实际上直接代码中调用call.cancel()的也意味着直接调用第三部的代码。
AsyncCall线程,而这个call.get()
的则是返回一个我们开头介绍的call这个调用对象,cancel()的内部实现如下:
第一步:Dispatcher。cancelAll()
public synchronized void cancelAll() { for (AsyncCall call : readyAsyncCalls) { call.get().cancel(); } for (AsyncCall call : runningAsyncCalls) { call.get().cancel(); } for (RealCall call : runningSyncCalls) { call.cancel(); }}
第二步:AsyncCall.cancel()
RealCall get() { return RealCall.this;}
第三步:RealCall.cancel()
@Override public void cancel() { retryAndFollowUpInterceptor.cancel();}
三 结束
既然一个线程池队列能提供add和remove肯定也会提供线程执行完毕的方法,Dispatcher则提供了
finished(),代码如下:
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();//监测内部是否有空线程,执行的大于max限制等 runningCallsCount = runningCallsCount(); idleCallback = this.idleCallback; } if (runningCallsCount == 0 && idleCallback != null) { idleCallback.run(); }}
好了 ,该到总结的时候了,Dispatcher
实际上没开发者认为的这么神秘
他持有这一个可生产线程池的service和三个可代表的线程维护的队列,并提供了add,remove,cancel三个性质的方法,而只不过这个取消的方法交给了拦截器处理,拦截器调用Conection接口进行传输层的握手中断。并执行callBack的取消接口提供给上层。
拦截器和Conection
见上一章的介绍。
疑问:
1 为什么dispather没提供怎么请求的功能
2 为什么dispather不进行缓存处理?
dipathper里面持有的线程,则进行自己的请求处理,不受dispatchper
管控,实际上就是线程自己在执行。
自己的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 { responseCallback.onFailure(RealCall.this, e); } } finally { client.dispatcher().finished(this); } }}
第二个问题,等我们学完第三篇 okhttp的缓存Cache
,自然迎刃而解。
Tamic原创,更多其他关注微信公众号 开发者技术前线。
- OkHttp 3.x 源码解析之Dispatcher
- OkHttp 3.x 源码解析之Interceptor 拦截器
- OkHttp源码解析之概述
- OKhttp源码解析---拦截器之RetryAndFollowUpInterceptor
- OKhttp源码解析---拦截器之BridgeInterceptor
- OKhttp源码解析---拦截器之CacheInterceptor
- OKhttp源码解析---拦截器之ConnectInterceptor
- OKhttp源码解析---拦截器之CallServerInterceptor
- Okhttp源码解析之Interceptor(拦截器)
- 在okHttp里使用Gson解析 报错OKHttp Dispatcher
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- 对抗样本论文学习:Deep Neural Networks are Easily Fooled
- 支持图片无限轮播的BannerLayout
- ps图片黑白调整算法——java实现及性能优化
- windows 上初次安装 node,react
- 菱形代码
- OkHttp 3.x 源码解析之Dispatcher
- 前端常用插件
- 鸡兔同笼
- 17.笔记 MySQL学习——MYSQL常用操作命令数据库相关
- 《Scalable and Sustainable Deep Learning via Randomized Hashing》 阅读报告
- Veizheng20171114
- nginx外网映射工具连接不上新处理方案
- Ajax之jQuery.serialize()
- 从服务器上下载压缩包