Okhhtp是square公司出品的一款非常优秀的网络框架。可以说,目前市面上大部分Android开发人员在网络请求事,都在使用这款优秀的框架。下面,我就结合源码来分析下这款网络框架内部的执行原理,做到知其然也之气所以然的目的。
OkHttp的使用非常的简单,我下面写一个大概的使用方法提供给大家查看。
//创建okhttpclient对象 OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); //创建请求,里面可以设置get、post请求和请求地址和请求头等一系列操作 Request request = new Request.Builder() .url("www.baidu.com") .build(); //获取到一个call,这其实是一个线程请求任务,由其发起真正的网络请求 Call call = okHttpClient.newCall(request); //同步请求 try { call.execute(); } catch (IOException e) { e.printStackTrace(); } //异步请求 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { //TODO 异步请求失败的操作 } @Override public void onResponse(Call call, Response response) throws IOException { //TODO 同步请求成功的操作 } }); 复制代码
可以看到,OkHttp的使用并不复杂,按照上面的一套流程下来就可以执行一次请求。由于这篇文章重点在于分析源码,所以对于OkHttp的使用并不做过多的描述。
下面开始进入分析源码状态。
dispatcher分发器可以说是OkHttp的重要部分之一,用一句话来说明他的用途,就是他是用来管理同步和异步请求的。
我们点击OkhttpClient的Builder()进去看下源码
public Builder() { 1 dispatcher = new Dispatcher(); 2 protocols = DEFAULT_PROTOCOLS; 3 connectionSpecs = DEFAULT_CONNECTION_SPECS; 4 eventListenerFactory = EventListener.factory(EventListener.NONE); 5 proxySelector = ProxySelector.getDefault(); 6 cookieJar = CookieJar.NO_COOKIES; 7 socketFactory = SocketFactory.getDefault(); 8 hostnameVerifier = OkHostnameVerifier.INSTANCE; 9 certificatePinner = CertificatePinner.DEFAULT; 10 proxyAuthenticator = Authenticator.NONE; 11 authenticator = Authenticator.NONE; 12 connectionPool = new ConnectionPool(); 13 dns = Dns.SYSTEM; 14 followSslRedirects = true; 15 followRedirects = true; 16 retryOnConnectionFailure = true; 17 connectTimeout = 10_000; 18 readTimeout = 10_000; 19 writeTimeout = 10_000; 20 pingInterval = 0; } 复制代码
可以看到在标记为1的地方有一个 dispatcher = new Dispatcher() ,原来分发器Dispatcher是在OkHttpClient的Builder中创建的。
然后点击Dispatcher进去看下Dispatcher类的内部代码
public final class Dispatcher { private int maxRequests = 64; private int maxRequestsPerHost = 5; private Runnable idleCallback; /** 线程池 */ private ExecutorService executorService; /** 准备执行任务的异步队列 */ private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); /** 正在执行任务的异步队列 */ private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); /** 正在执行任务的同步队列*/ private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); } 复制代码
删减了一些代码,我们只看他的内部的声明的一些变量。可以看到,在Dispatcher的内部其实声明了四个东西,三个任务队列,分为是:
准备执行任务的异步队列 readyAsyncCalls,
正在执行任务的异步队列 runningAsyncCalls,
正在执行任务的同步队列 runningSyncCalls;
和一个线程池 executorService;
我们点击dispatcher.execute()的execute()方法进去查看源码
@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); } } 复制代码
1.这段代码首先用一个同步锁synchronize来修饰代码块,代表这段代码只能是串行状态并不能支持并行。
2.其次再进行一次if判断,判断任务是否在执行,如果正在执行的任务再次进行执行,那么便会抛出异常"Already Executed",可见OkHttp不能重复进行任务的执行。
3.紧接着调用了dispatcher.execute(this)来执行同步任务
4.接着调用一个连接器链getResponseWithInterceptorChain(),将遍历所有的拦截器,最后将结果result返回。(关于拦截器后面再讲,这里不做论述)
5.最后再次调用dispatcher.finished(this)来结束所有的请求。
那么这个流程我们重点分析第3步和第5步
我们点击第3步的execute(this)方法里面查看里面做了什么
synchronized void executed(RealCall call) { runningSyncCalls.add(call); } 复制代码
可以看到,这个方法仍然用synchronize来进行了修饰使得多线程也只能串行。之后将RealCall这个任务添加放入runningSyncCalls这个运行时同步任务队列中。
可以看出,execute()主要作用就是将任务添加到任务队列当中去,那么添加进去之后在哪里执行呐?这就要到第5步的dispatcher.finish(this)方法中去看了。
点击finish(this)进入里面查看代码
void finished(RealCall call) { finished(runningSyncCalls, call, false); } private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) { 1 int runningCallsCount; 2 Runnable idleCallback; 3 synchronized (this) { 4 if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); 5 if (promoteCalls) promoteCalls(); 6 runningCallsCount = runningCallsCount(); 7 idleCallback = this.idleCallback; 8 } 9 if (runningCallsCount == 0 && idleCallback != null) { 10 idleCallback.run(); } } 复制代码
具体我们需要看的是 finished(Deque calls, T call, boolean promoteCalls)
前面几行都只是做一些判断,我们重点看第5行的代码,里面有一个判断if(promoteCalls) promoteCalls();
如果promoteCalls为true,那么就去执行promoteCalls()方法。那么重点再promoteCalls()方法里面到底做了什么。我们再点击进去看看
private void promoteCalls() { 1 if (runningAsyncCalls.size() >= maxRequests) return; 2 if (readyAsyncCalls.isEmpty()) return; 3 for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { 4 AsyncCall call = i.next(); 5 if (runningCallsForHost(call) < maxRequestsPerHost) { 6 i.remove(); 7 runningAsyncCalls.add(call); 8 executorService().execute(call); 9 } 10 if (runningAsyncCalls.size() >= maxRequests) return; } } 复制代码
可以看到前面1,2两步都是在判断,判断运行时异步队列的最大值是否超过64,超过则return结束掉。如果异步准备队列是空(isEmpty()),那么也return结束掉。
第3步开始进行for循环,从等待队列中取出任务,添加到运行队列runningAsyncCalls中去(runningAsyncCalls.add(call);),然后再把call任务交给线程池去执行(executorService().execute(call)),从而完成一次请求。
我们再来看下异步请求
//异步请求 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { //TODO 异步请求失败的操作 } @Override public void onResponse(Call call, Response response) throws IOException { //TODO 同步请求成功的操作 } }); 复制代码
我们点击enqueue()方法进去看下代码
void enqueue(Callback responseCallback); 复制代码
看到只有一行代码,很简洁
@Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); client.dispatcher().enqueue(new AsyncCall(responseCallback)); } 复制代码
我们再次跳到 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); } } 复制代码
可以看到,在这里面我们进行了一次判断,如果正在运行的异步队列里面的任务小于maxRequests并且runningCallsForHost(call) < maxRequestsPerHost则往runningAsyncCalls 中添加任务,然后交给线程池去执行,不然则往异步准备队列中添加任务,作为等待。
那么maxRequests和maxRequestsPerHost是啥?我们在分析Dispatcher的时候看到在这个类初始化了几个变量其中就有这两个变量
public final class Dispatcher { private int maxRequests = 64; private int maxRequestsPerHost = 5; } 复制代码
看到maxRequests为64,代表最大异步请求数量为64,最大的host数为5个,如果运行任务队列中超过了这两个数量,则不能把任务交给线程池去执行,而是直接放到readyAsyncCalls队列中去等待。
@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); } } } 复制代码
可以看到这里仍然通过一次连接器链将结果返回;
然后有几次判断,判断是否是需要重定向重连等,如果retryAndFollowUpInterceptor.isCanceled()代表连接取消失败,则回调responseCallback.onFailure(RealCall.this, new IOException("Canceled"));如果成功则回调responseCallback.onResponse(RealCall.this, response);
最后又回到了finish(this)方法,这个之前说过了,就是把运行队列中的内容拿出来交给线程池去执行。
异步请求
1.一开始会去执行dispatcher.enqueue(),里面会有两个判断,判断运行任务列队runningAsyncCalls中的任务总数是否超过64个以及网络请求的host是否超过5个,如果没超过这两个数,那么往运行任务队列中添加任务,然后把任务交给线程池去执行。如果超过了,则直接放到准备等待任务队列中readyAsyncCalls中去等待。
2.之后执行一次拦截器链getResponseIntecepterChain()把结果返回返回
3.在进行一次判断,这个任务是否需要重新连接,是否被取消retryAndFollowUpInterceptor.isCanceled(),如果取消了则证明这个任务执行失败了,则在线程中回调onFailure()方法,如果成功则回调onResponse()方法。
4.最后调用dispatcher.finish(this)方法,里面有一个promoteCalls()方法,用来调整队列中的任务。通过把刚才准备队列中(readyAsyncCalls)中的任务再次添加到异步运行任务队列中去,然后交给线程池去执行。
这就是一次异步请求的流程。
后面我们再来分析拦截器和连接池这两个OkHttp的重点功能
未完待续。。。