OkHttp的实现原理(一)之同步
来源:互联网 发布:民航网络信息安全 编辑:程序博客网 时间:2024/05/17 23:43
最近我做的一个项目的网络框架就是选用的OkHttp,仅仅只是调用一下Api当然是不够的,想要驾驭它并灵活的运用则需要了解它的实现原理,那么就需要去看它的源码了。
Okhttp有两种请求方式:
1. 同步请求: execute();
2. 异步请求 :
public void enqueue(Callback responseCallback) { this.enqueue(responseCallback, false); }
不管是同步请求还是异步请求,最先开始的都是需要有一个Request对象,然后通过OkhttpClient实例会生成一个Call对象
(1) post请求 Request request = new Request.Builder().url(url).post(getRequestBody()).build(); get请求 Request request = new Request.Builder().url(url).get().build();(2) Call call = client.newCall(request);
这两步操作对于同步请求和异步请求是相同的,当然你可以往request对象里面加入请求头参数,缓存的设置等,这就看你自己的需求了。
我们先分析同步请求的方式:
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; }
首先会先判断这一次请求是否正在处理,如果是就抛出一个异常,如果不是才往下走,可见OkHttp是不允许对一个还没处理完的请求再次发送请求的情况出现的。接下来会看到这段代码this.client.getDispatcher().executed(this);
看语意是得到了一个分发器,这个分发器是在new OkHttpClient对象的时候初始化的
public OkHttpClient() { this.routeDatabase = new RouteDatabase(); this.dispatcher = new Dispatcher(); }
得到了分发器后调用executed(this);这个this就是我们一开始生成的Call对象,
synchronized void executed(Call call) { this.executedCalls.add(call); }
是将这个call对象添加到了executedCalls队列中(底层使用数组实现的),我们继续往下面看
Response result = this.getResponseWithInterceptorChain(false);` if(result == null) { throw new IOException("Canceled"); } var2 = result; } finally { this.client.getDispatcher().finished(this); } return var2;
这段代码是将result返回,看来这个result就是我们从服务器拿到的数据,好吧,那么这个getResponseWithInterceptorChain(false);
方法就很关键了啊
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException { Call.ApplicationInterceptorChain chain = new Call.ApplicationInterceptorChain(0, this.originalRequest, forWebSocket); return chain.proceed(this.originalRequest); }
首先构造了一个ApplicationInterceptorChain对象chain ,看这写法就知道是Call的内部类,这个originalRequest其实就是我们之前的request对象,而forWebSocket = false 是我们调用这个方法的时候传进去的,然后看proceed(this.originalRequest)这个方法,这个方法就是发送请求并获得响应的方法了。
public Response proceed(Request request) throws IOException { if(this.index < Call.this.client.interceptors().size()) { Call.ApplicationInterceptorChain chain = Call.this.new ApplicationInterceptorChain(this.index + 1, request, this.forWebSocket); Interceptor interceptor = (Interceptor)Call.this.client.interceptors().get(this.index); Response interceptedResponse = interceptor.intercept(chain); if(interceptedResponse == null) { throw new NullPointerException("application interceptor " + interceptor + " returned null"); } else { return interceptedResponse; } } else { return Call.this.getResponse(request, this.forWebSocket); } }
因为这个index一开始传入的是0,所以先判断okHttpClient对象中是否有拦截器,如果有,就首先创建一个新的ApplicationInterceptorChain对象,跟之前的比就是将index+1传入,然后去interceptors集合里面取出拦截器,通过 Response interceptedResponse = interceptor.intercept(chain);放大获取响应并返回,这个方法是当你给OkHttpClient设置拦截器的时候会调用的方法,这个方法得由开发者自己去写。如果没有拦截器,那么就会调用Call.this.getResponse(request, this.forWebSocket);这个方法去获取响应了。
Response getResponse(Request request, boolean forWebSocket) throws IOException { RequestBody body = request.body(); if(body != null) { Builder followUpCount = request.newBuilder(); MediaType releaseConnection = body.contentType(); if(releaseConnection != null) { followUpCount.header("Content-Type", releaseConnection.toString()); } long response = body.contentLength(); if(response != -1L) { followUpCount.header("Content-Length", Long.toString(response)); 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, (StreamAllocation)null, (RetryableSink)null, (Response)null); int var20 = 0; while(!this.canceled) { boolean var21 = true; boolean var15 = false; StreamAllocation streamAllocation; label173: { label172: { try { HttpEngine followUp; try { var15 = true; this.engine.sendRequest(); this.engine.readResponse(); var21 = false; var15 = false; break label173; } catch (RequestException var16) { throw var16.getCause(); } catch (RouteException var17) { followUp = this.engine.recover(var17); if(followUp == null) { throw var17.getLastConnectException(); } } catch (IOException var18) { followUp = this.engine.recover(var18, (Sink)null); if(followUp != null) { var21 = false; this.engine = followUp; var15 = false; break label172; } throw var18; } var21 = false; this.engine = followUp; var15 = false; } finally { if(var15) { if(var21) { StreamAllocation streamAllocation1 = this.engine.close(); streamAllocation1.release(); } } } if(var21) { streamAllocation = this.engine.close(); streamAllocation.release(); } continue; } if(var21) { streamAllocation = this.engine.close(); streamAllocation.release(); } continue; } if(var21) { StreamAllocation var23 = this.engine.close(); var23.release(); } Response var22 = this.engine.getResponse(); Request var24 = this.engine.followUpRequest(); if(var24 == null) { if(!forWebSocket) { this.engine.releaseStreamAllocation(); } return var22; } streamAllocation = this.engine.close(); ++var20; if(var20 > 20) { streamAllocation.release(); throw new ProtocolException("Too many follow-up requests: " + var20); } if(!this.engine.sameConnection(var24.httpUrl())) { streamAllocation.release(); streamAllocation = null; } this.engine = new HttpEngine(this.client, var24, false, false, forWebSocket, streamAllocation, (RetryableSink)null, var22); } this.engine.releaseStreamAllocation(); throw new IOException("Canceled"); }
首先判断该请求是否是Post请求,如果是,就会对该request对象做一些处理,随后会创建一个HttpEngine对象,将okHttpClient对象和request对象forWebSocket = false作为参数传入进去了。
this.engine = new HttpEngine(this.client, request, false, false, forWebSocket, (StreamAllocation)null, (RetryableSink)null, (Response)null);
之后会进入一个循环,进入该循环的条件就是该请求没有被取消掉,
this.engine.sendRequest(); this.engine.readResponse();
这两句代码是不是很好理解啊,不就是engine发送了请求,然后读响应吗,然后会跳出标签label173,接着看92行代码 Response var22 = this.engine.getResponse();好家伙,这里就得到响应了,你肯定不爽,怎么这么快就发送请求和获得响应了啊?都还没看到sendRequest(),getResponse()这两个方法内部做了什么操作呢,好吧,那么我必须要来满足你:
public void sendRequest() throws RequestException, RouteException, IOException { if(this.cacheStrategy == null) { if(this.httpStream != null) { throw new IllegalStateException(); } else { Request request = this.networkRequest(this.userRequest); InternalCache responseCache = Internal.instance.internalCache(this.client); Response cacheCandidate = responseCache != null?responseCache.get(request):null; long now = System.currentTimeMillis(); this.cacheStrategy = (new Factory(now, request, cacheCandidate)).get(); this.networkRequest = this.cacheStrategy.networkRequest; this.cacheResponse = this.cacheStrategy.cacheResponse; if(responseCache != null) { responseCache.trackResponse(this.cacheStrategy); } if(cacheCandidate != null && this.cacheResponse == null) { Util.closeQuietly(cacheCandidate.body()); } if(this.networkRequest != null) { this.httpStream = this.connect(); this.httpStream.setHttpEngine(this); if(this.callerWritesRequestBody && this.permitsRequestBody(this.networkRequest) && this.requestBodyOut == null) { long contentLength = OkHeaders.contentLength(request); if(this.bufferRequestBody) { if(contentLength > 2147483647L) { throw new IllegalStateException("Use setFixedLengthStreamingMode() or setChunkedStreamingMode() for requests larger than 2 GiB."); } if(contentLength != -1L) { this.httpStream.writeRequestHeaders(this.networkRequest); this.requestBodyOut = new RetryableSink((int)contentLength); } else { this.requestBodyOut = new RetryableSink(); } } else { this.httpStream.writeRequestHeaders(this.networkRequest); this.requestBodyOut = this.httpStream.createRequestBody(this.networkRequest, contentLength); } } } else { this.streamAllocation.release(); if(this.cacheResponse != null) { this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).build(); } else { this.userResponse = (new Builder()).request(this.userRequest).priorResponse(stripBody(this.priorResponse)).protocol(Protocol.HTTP_1_1).code(504).message("Unsatisfiable Request (only-if-cached)").body(EMPTY_BODY).build(); } this.userResponse = this.unzip(this.userResponse); } } } }
6-19行:获取用户设置的缓存策略。
21-41行:需要从网络上获取数据。
43 - 50行:从缓存中获取数据。这个userResponse 就是缓存从中的拿到的数据。
我们主要看从网络上获取数据这部分内容,要从服务器上获取内容首先要跟服务器建立起连接吧,那么我们来看22行connet();
private HttpStream connect() throws RouteException, RequestException, IOException { boolean doExtensiveHealthChecks = !this.networkRequest.method().equals("GET"); return this.streamAllocation.newStream(this.client.getConnectTimeout(), this.client.getReadTimeout(), this.client.getWriteTimeout(), this.client.getRetryOnConnectionFailure(), doExtensiveHealthChecks); }
doExtensiveHealthChecks = false 说明该请求是GET请求,doExtensiveHealthChecks = ture 说明是POST请求,newStream(this.client.getConnectTimeout(), this.client.getReadTimeout(), this.client.getWriteTimeout(), this.client.getRetryOnConnectionFailure(), doExtensiveHealthChecks);这个方法就是建立连接的方法,让我们看一下:
public HttpStream newStream(int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks) throws RouteException, IOException { try { RealConnection e = this.findHealthyConnection(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks); Object resultStream; if(e.framedConnection != null) { resultStream = new Http2xStream(this, e.framedConnection); } else { e.getSocket().setSoTimeout(readTimeout); e.source.timeout().timeout((long)readTimeout, TimeUnit.MILLISECONDS); e.sink.timeout().timeout((long)writeTimeout, TimeUnit.MILLISECONDS); resultStream = new Http1xStream(this, e.source, e.sink); } ConnectionPool var8 = this.connectionPool; synchronized(this.connectionPool) { ++e.streamCount; this.stream = (HttpStream)resultStream; return (HttpStream)resultStream; } } catch (IOException var11) { throw new RouteException(var11); } }
RealConnection 这个就是真的连接对象了吧,findHealthyConnection这个方法里面会创建连接对象,然后建立连接会调用newConnection.connect(connectTimeout, readTimeout, writeTimeout, this.address.getConnectionSpecs(), connectionRetryEnabled);这个方法,最后会返回这个连接对象就是RealConnection e这个对象了,让我们看看他是如何建立连接的吧
public void connect(int connectTimeout, int readTimeout, int writeTimeout, List<ConnectionSpec> connectionSpecs, boolean connectionRetryEnabled) throws RouteException { if(this.protocol != null) { throw new IllegalStateException("already connected"); } else { RouteException routeException = null; ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs); Proxy proxy = this.route.getProxy(); Address address = this.route.getAddress(); if(this.route.getAddress().getSslSocketFactory() == null && !connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) { throw new RouteException(new UnknownServiceException("CLEARTEXT communication not supported: " + connectionSpecs)); } else { while(this.protocol == null) { try { this.rawSocket = proxy.type() != Type.DIRECT && proxy.type() != Type.HTTP?new Socket(proxy):address.getSocketFactory().createSocket(); this.connectSocket(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector); } catch (IOException var11) { Util.closeQuietly(this.socket); Util.closeQuietly(this.rawSocket); this.socket = null; this.rawSocket = null; this.source = null; this.sink = null; this.handshake = null; this.protocol = null; if(routeException == null) { routeException = new RouteException(var11); } else { routeException.addConnectException(var11); } if(!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(var11)) { throw routeException; } } } } } }
Address address = this.route.getAddress();获得需要连接到服务器的地址,第14行,创建了Socket对象,所以okHttp是Socket来进行连接的。有了Socket就可以获取输入输出流了,建立了了连接以后你会发现会走下面两句代码:
this.httpStream.writeRequestHeaders(this.networkRequest); this.requestBodyOut = this.httpStream.createRequestBody(this.networkRequest, contentLength);
httpStream这个对象是什么呢?我们知道http协议网络数据通信,其实就是客户端将请求数据以请求报文的格式发送给服务器,服务器获取请求后,执行相应的处理,然后将返回的结果以响应报文的格式返回给客户端,httpStream就是用来写请求报文和读取响应报文的。
我们再来看看engine.getResponse();获取响应的方法:
public Response getResponse() { if(this.userResponse == null) { throw new IllegalStateException(); } else { return this.userResponse; } }
就是将userResponse返回了,userResponse是什么呢?我们还记得如果是从缓存中拿的数据,那么
if(this.cacheResponse != null) { this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).build(); } else { this.userResponse = (new Builder()).request(this.userRequest).priorResponse(stripBody(this.priorResponse)).protocol(Protocol.HTTP_1_1).code(504).message("Unsatisfiable Request (only-if-cached)").body(EMPTY_BODY).build(); } this.userResponse = this.unzip(this.userResponse); }
此时userResponse 确实被赋值了,但是如果从网络上请求数据,那么这个userResponse 是什么时候被赋值的呢?不知道大家对 this.engine.readResponse();感觉到奇怪没有,它在发送请求之后,获取响应之前调用,我一开始就得挺奇怪的,我以为这个方法就是获取响应的方法,但是它没有返回值啊,它到底是来干嘛的呢?只有看源码了
public void readResponse() throws IOException { if(this.userResponse == null) { if(this.networkRequest == null && this.cacheResponse == null) { throw new IllegalStateException("call sendRequest() first!"); } else if(this.networkRequest != null) { Response networkResponse; if(this.forWebSocket) { this.httpStream.writeRequestHeaders(this.networkRequest); networkResponse = this.readNetworkResponse(); } else if(!this.callerWritesRequestBody) { networkResponse = (new HttpEngine.NetworkInterceptorChain(0, this.networkRequest)).proceed(this.networkRequest); } else { if(this.bufferedRequestBody != null && this.bufferedRequestBody.buffer().size() > 0L) { this.bufferedRequestBody.emit(); } if(this.sentRequestMillis == -1L) { if(OkHeaders.contentLength(this.networkRequest) == -1L && this.requestBodyOut instanceof RetryableSink) { long responseCache = ((RetryableSink)this.requestBodyOut).contentLength(); this.networkRequest = this.networkRequest.newBuilder().header("Content-Length", Long.toString(responseCache)).build(); } this.httpStream.writeRequestHeaders(this.networkRequest); } if(this.requestBodyOut != null) { if(this.bufferedRequestBody != null) { this.bufferedRequestBody.close(); } else { this.requestBodyOut.close(); } if(this.requestBodyOut instanceof RetryableSink) { this.httpStream.writeRequestBody((RetryableSink)this.requestBodyOut); } } networkResponse = this.readNetworkResponse(); } this.receiveHeaders(networkResponse.headers()); if(this.cacheResponse != null) { if(validate(this.cacheResponse, networkResponse)) { this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).headers(combine(this.cacheResponse.headers(), networkResponse.headers())).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build(); networkResponse.body().close(); this.releaseStreamAllocation(); InternalCache responseCache1 = Internal.instance.internalCache(this.client); responseCache1.trackConditionalCacheHit(); responseCache1.update(this.cacheResponse, stripBody(this.userResponse)); this.userResponse = this.unzip(this.userResponse); return; } Util.closeQuietly(this.cacheResponse.body()); } this.userResponse = networkResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build(); if(hasBody(this.userResponse)) { this.maybeCache(); this.userResponse = this.unzip(this.cacheWritingResponse(this.storeRequest, this.userResponse)); } } } }
6 - 38 行 : 根据不同的条件获取响应networkResponse 。
42 - 60行 :就是真正的对userResponse 进行赋值操作了。
好了,到这里有关OkHttp的同步请求方式的源码就全部分析完了,下一次我将继续分析OkHttp异步请求的原理,最后我想用一张图来简单的总结一下同步请求的流程:
- OkHttp的实现原理(一)之同步
- OkHttp的实现原理(二)之异步
- okhttp 一 概述及同步和异步请求的实现
- okhttp的学习(一)
- (4.2.36.1)HTTP之OkHttp(一): Okhttp使用详解
- MySQL主从同步--原理及实现(一)
- OkHttp的同步和异步请求的实现
- OkHttp源码阅读之旅(一)
- okhttp中的Okio实现原理
- (4.2.36.2)HTTP之OkHttp(二): Okhttp的封装
- okhttp的简单使用(一)
- Android中Okhttp的使用(一)
- OkHttp的学习(一)
- 面试准备之okhttp的使用及源码分析(一)
- 关于Okhttp(一)
- OKHttp(一)入门
- 初识OkHttp(一)
- OkHttp完全解析(一)OkHttp简介
- 框架梳理|企业大数据管理之道
- VC下寻找某个进程并关闭
- 并查集详解
- MySql性能优化一
- kernel 系统调用----system call
- OkHttp的实现原理(一)之同步
- jQuery方法扩展代码整理
- 程序员的鄙视链
- yarn资源隔离
- mysql无法更改初始密码,mysql忘记登录密码
- js点击复制文本
- ProtocolBuffer Java Jar 生成指导
- Git推送报错:The remote end hung up unexpectedly的解决办法
- MySQL数据库悲观锁总结和实践