这一篇我们将从源码角度来看下OKHttp是如何完成一些列的网络的操作的。
我们知道在okhttpclient同步请求和异步请求调用的接口不一样,但它们最后都是殊途同归地走到Call里面的
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException { Call.ApplicationInterceptorChain chain = new Call.ApplicationInterceptorChain(0, this.originalRequest, forWebSocket); return chain.proceed(this.originalRequest); }
首先我们来看看调用execute和enqueue是如何走到`getResponseWithInterceptorChain的。
public Response execute() throws IOException { synchronized(this) { if(this.executed) { throw new IllegalStateException("Already Executed"); } this.executed = true; } Response var2; try { this.client.getDispatcher().executed(this); Response result = this.getResponseWithInterceptorChain(false); if(result == null) { throw new IOException("Canceled"); } var2 = result; } finally { this.client.getDispatcher().finished(this); } return var2; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
上面代码14行用到了一个从OkHttpClient获得的Dispatcher然后把它加入到分发器里面的队列 executedCalls中,在完成的时候会remove掉
synchronized void executed(Call call) { this.executedCalls.add(call); } synchronized void finished(Call call) { if(!this.executedCalls.remove(call)) { throw new AssertionError("Call wasn\'t in-flight!"); } }
再来看看异步如何走到getResponseWithInterceptorChain
public void enqueue(Callback responseCallback) { this.enqueue(responseCallback, false); } void enqueue(Callback responseCallback, boolean forWebSocket) { synchronized(this) { if(this.executed) { throw new IllegalStateException("Already Executed"); } this.executed = true; } this.client.getDispatcher().enqueue(new Call.AsyncCall(responseCallback, forWebSocket, null)); }
是不是和同步很像,最后都是调用分发器的enqueue,但和同步不同的是,同步传入enqueue方法的参数是Call,异步传入的是AsyncCall,这个是什么呢,这个是Call里面的一个内部类,而且是一个继承了Runnable的内部类,我们先来看看这个execute怎么操作
synchronized void enqueue(AsyncCall call) { //判断当前运行的线程是否超过最大线程数,以及同一个请求是否要超过相同请求同时存在的最大数目 if(this.runningCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) { this.runningCalls.add(call) //将请求放到线程池里运行 this.getExecutorService().execute(call) } else { //不满足运行条件放到后备队列里 this.readyCalls.add(call) } }
从上面代码我们看到异步请求是有条件限制的,默认最多64个请求,而同一个请求默认最多同时存在5个
private int runningCallsForHost(AsyncCall call) { int result = 0; Iterator var3 = this.runningCalls.iterator(); while(var3.hasNext()) { AsyncCall c = (AsyncCall)var3.next(); 通过比较每个请求的url,一样代表同一个请求 if(c.host().equals(call.host())) { ++result; } } return result; }
下面的两个参数可以通过从OKHttpClient getDispatch得到分发器,并根据分发器提提供的设置方法去修改,修改比较简单就不贴代码了 。
private int maxRequests = 64; private int maxRequestsPerHost = 5;
再来看看AsyncCall 的run里面的代码
public final void run() { String oldName = Thread.currentThread().getName(); Thread.currentThread().setName(this.name); try { this.execute(); } finally { Thread.currentThread().setName(oldName); } }
显然AsyncCall的execute才是核心
protected void execute() { boolean signalledCallback = false try { Response e = Call.this.getResponseWithInterceptorChain(this.forWebSocket) if(Call.this.canceled) { signalledCallback = true//取消call调用onFailure回调 this.responseCallback.onFailure(Call.this.originalRequest, new IOException("Canceled")) } else { signalledCallback = true //请求成功,回调onResponse this.responseCallback.onResponse(e) } } catch (IOException var6) { if(signalledCallback) { Internal.logger.log(Level.INFO, "Callback failure for " + Call.this.toLoggableString(), var6) } else { this.responseCallback.onFailure(Call.this.engine.getRequest(), var6) } } finally { //完成请求,调用finish,从队列中清空 Call.this.client.getDispatcher().finished(this) } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
在代码第五行我们又看到了getResponseWithInterceptorChain。
我们继续看getResponseWithInterceptorChain里面的实现
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException { Call.ApplicationInterceptorChain chain = new Call.ApplicationInterceptorChain(0, this.originalRequest, forWebSocket); return chain.proceed(this.originalRequest); }
创建了一个ApplicationInterceptorChain ,并且第一个参数传入0,这个0是有特殊用法的,涉及到OKHttp里面的一个功能叫做拦截器,从getResponseWithInterceptorChain这个名字里其实也能看出一二。先看看proceed做了什么
public Response proceed(Request request) throws IOException { //先判断是否每个拦截器都有对应的处理,没有的话先继续新建ApplicationInterceptorChain ,并执行当前拦截器的intercept方法 if(this.index < Call.this.client.interceptors().size()) { Call.ApplicationInterceptorChain chain = Call.this.new ApplicationInterceptorChain(this.index + 1, request, this.forWebSocket) return ((Interceptor)Call.this.client.interceptors().get(this.index)).intercept(chain) } else { return Call.this.getResponse(request, this.forWebSocket) } }
这里碰到一个拦截器,OKHttp增加了一个拦截器机制,先来看看官方文档对Interceptors 的解释
Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.
解释下就是拦截器可以用来转换,重试,重写请求的机制,再来看看官方文档里提供的例子
首先自定义一个拦截器用于打印一些发送信息
class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); long t1 = System.nanoTime(); logger.info(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers())); Response response = chain.proceed(request); long t2 = System.nanoTime(); logger.info(String.format("Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers())); return response; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
然后使用的时候把它添加到okhttpclient
OkHttpClient client = new OkHttpClient();client.interceptors().add(new LoggingInterceptor());Request request = new Request.Builder() .url("http://www.publicobject.com/helloworld.txt") .header("User-Agent", "OkHttp Example") .build();Response response = client.newCall(request).execute();response.body().close();
当我们执行到proceed,就会去判断是否有拦截器有的话先执行拦截器里的intercept,而在intercept里一般会进行一些自定义操作并且调用procced去判断是否要继续执行拦截器操作还是直接去获取网络请求,我们看看procced做了什么
public Response proceed(Request request) throws IOException {//判断还有拦截器需要执行不,生成新的ApplicationInterceptorChain并调用它的intercept去执行用户定义的操作 if(this.index < Call.this.client.interceptors().size()) { Call.ApplicationInterceptorChain chain = Call.this.new ApplicationInterceptorChain(this.index + 1, request, this.forWebSocket) return ((Interceptor)Call.this.client.interceptors().get(this.index)).intercept(chain) } else { return Call.this.getResponse(request, this.forWebSocket) } }
上面的例子执行结果如下
INFO: Sending request http://www.publicobject.com/helloworld.txt on nullUser-Agent: OkHttp ExampleINFO: Received response for https://publicobject.com/helloworld.txt in 1179.7msServer: nginx/1.4.6 (Ubuntu)Content-Type: text/plainContent-Length: 1759Connection: keep-alive
拦截器的整个机制如下图
上图中把拦截器氛围应用拦截器和网络拦截器,其实这个取决于你在拦截器里做了哪方面的操作,比如改变了请求头部之类的就可以成为网络拦截器。
在处理完拦截器操作后,就进入到重要的getResponse方法,真正的去进行发送请求,处理请求,接收返回结果。
Response getResponse(Request request, boolean forWebSocket) throws IOException { RequestBody body = request.body(); if(body != null) { Builder followUpCount = request.newBuilder(); MediaType response = body.contentType(); if(response != null) { followUpCount.header("Content-Type", response.toString()); } long followUp = body.contentLength(); if(followUp != -1L) { followUpCount.header("Content-Length", Long.toString(followUp)); followUpCount.removeHeader("Transfer-Encoding"); } else { followUpCount.header("Transfer-Encoding", "chunked"); followUpCount.removeHeader("Content-Length"); } request = followUpCount.build(); } this.engine = new HttpEngine(this.client, request, false, false, forWebSocket, (Connection)null, (RouteSelector)null, (RetryableSink)null, (Response)null); int var11 = 0; while(!this.canceled) { HttpEngine var13; try { this.engine.sendRequest(); this.engine.readResponse(); } catch (RequestException var8) { throw var8.getCause(); } catch (RouteException var9) { var13 = this.engine.recover(var9); if(var13 != null) { this.engine = var13; continue; } throw var9.getLastConnectException(); } catch (IOException var10) { var13 = this.engine.recover(var10, (Sink)null); if(var13 != null) { this.engine = var13; continue; } throw var10; } Response var12 = this.engine.getResponse(); Request var14 = this.engine.followUpRequest(); if(var14 == null) { if(!forWebSocket) { this.engine.releaseConnection(); } return var12; } ++var11; if(var11 > 20) { throw new ProtocolException("Too many follow-up requests: " + var11); } if(!this.engine.sameConnection(var14.url())) { this.engine.releaseConnection(); } Connection connection = this.engine.close(); this.engine = new HttpEngine(this.client, var14, false, false, forWebSocket, connection, (RouteSelector)null, (RetryableSink)null, var12); } this.engine.releaseConnection(); throw new IOException("Canceled"); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
可以看到如果是post请求,先做一定的头部处理,然后新建一个HttpEngine去处理具体的操作,通过sendRequest发送具体请求操作,readResponse对服务器的答复做一定处理,在代码52行处getResponse得到从服务器返回的Response,讲到这里,我们整个的流程大概疏通了,代码贴了很多,简单的可以用下面一张图概括
整体看就是根据用户的请求Request放入到指定队列,并使用拦截器链的方式去执行我们的请求操作。