OKHttp源码解析(三)
来源:互联网 发布:淘宝p图用什么软件 编辑:程序博客网 时间:2024/05/29 16:20
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">readResponse</span>() <span class="hljs-keyword">throws</span> IOException { <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.userResponse == <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.networkRequest == <span class="hljs-keyword">null</span> && <span class="hljs-keyword">this</span>.cacheResponse == <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalStateException(<span class="hljs-string">"call sendRequest() first!"</span>); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.networkRequest != <span class="hljs-keyword">null</span>) { Response networkResponse; <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.forWebSocket) { <span class="hljs-keyword">this</span>.transport.writeRequestHeaders(<span class="hljs-keyword">this</span>.networkRequest); networkResponse = <span class="hljs-keyword">this</span>.readNetworkResponse(); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(!<span class="hljs-keyword">this</span>.callerWritesRequestBody) { <span class="hljs-comment">//利用拦截器方式去做答复处理</span> networkResponse = (<span class="hljs-keyword">new</span> HttpEngine.NetworkInterceptorChain(<span class="hljs-number">0</span>, <span class="hljs-keyword">this</span>.networkRequest)).proceed(<span class="hljs-keyword">this</span>.networkRequest); } <span class="hljs-keyword">else</span> { <span class="hljs-comment">//这个else分句可以不看</span> } <span class="hljs-keyword">this</span>.receiveHeaders(networkResponse.headers()); <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.cacheResponse != <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">if</span>(validate(<span class="hljs-keyword">this</span>.cacheResponse, networkResponse)) { <span class="hljs-keyword">this</span>.userResponse = <span class="hljs-keyword">this</span>.cacheResponse.newBuilder().request(<span class="hljs-keyword">this</span>.userRequest).priorResponse(stripBody(<span class="hljs-keyword">this</span>.priorResponse)).headers(combine(<span class="hljs-keyword">this</span>.cacheResponse.headers(), networkResponse.headers())).cacheResponse(stripBody(<span class="hljs-keyword">this</span>.cacheResponse)).networkResponse(stripBody(networkResponse)).build(); networkResponse.body().close(); <span class="hljs-keyword">this</span>.releaseConnection(); InternalCache responseCache1 = Internal.instance.internalCache(<span class="hljs-keyword">this</span>.client); responseCache1.trackConditionalCacheHit(); responseCache1.update(<span class="hljs-keyword">this</span>.cacheResponse, stripBody(<span class="hljs-keyword">this</span>.userResponse)); <span class="hljs-keyword">this</span>.userResponse = <span class="hljs-keyword">this</span>.unzip(<span class="hljs-keyword">this</span>.userResponse); <span class="hljs-keyword">return</span>; } Util.closeQuietly(<span class="hljs-keyword">this</span>.cacheResponse.body()); } <span class="hljs-keyword">this</span>.userResponse = networkResponse.newBuilder().request(<span class="hljs-keyword">this</span>.userRequest).priorResponse(stripBody(<span class="hljs-keyword">this</span>.priorResponse)).cacheResponse(stripBody(<span class="hljs-keyword">this</span>.cacheResponse)).networkResponse(stripBody(networkResponse)).build(); <span class="hljs-keyword">if</span>(hasBody(<span class="hljs-keyword">this</span>.userResponse)) { <span class="hljs-keyword">this</span>.maybeCache(); <span class="hljs-keyword">this</span>.userResponse = <span class="hljs-keyword">this</span>.unzip(<span class="hljs-keyword">this</span>.cacheWritingResponse(<span class="hljs-keyword">this</span>.storeRequest, <span class="hljs-keyword">this</span>.userResponse)); } } } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li></ul>
在上篇文章结尾OKHttp源码解析(二) 我们分析到这里第10行的callerWritesRequestBody会为空,所以我会直接进去这个判断,同样在OKHttp源码解析(一) 我们分析过发送请求时使用的拦截器模式,这里对答复的操作也用了同样的方式,不同于请求调用的是intercept,这里用的是proceed,我们就来看看这个方法做了什么
<code class="hljs avrasm has-numbering">public Response proceed(Request request) throws IOException { ++this<span class="hljs-preprocessor">.calls</span><span class="hljs-comment">;</span> if(this<span class="hljs-preprocessor">.index</span> > <span class="hljs-number">0</span>) { Interceptor response = (Interceptor)HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.client</span><span class="hljs-preprocessor">.networkInterceptors</span>()<span class="hljs-preprocessor">.get</span>(this<span class="hljs-preprocessor">.index</span> - <span class="hljs-number">1</span>)<span class="hljs-comment">;</span> Address code = this<span class="hljs-preprocessor">.connection</span>()<span class="hljs-preprocessor">.getRoute</span>()<span class="hljs-preprocessor">.getAddress</span>()<span class="hljs-comment">;</span> if(!request<span class="hljs-preprocessor">.url</span>()<span class="hljs-preprocessor">.getHost</span>()<span class="hljs-preprocessor">.equals</span>(code<span class="hljs-preprocessor">.getUriHost</span>()) || Util<span class="hljs-preprocessor">.getEffectivePort</span>(request<span class="hljs-preprocessor">.url</span>()) != code<span class="hljs-preprocessor">.getUriPort</span>()) { throw new IllegalStateException(<span class="hljs-string">"network interceptor "</span> + response + <span class="hljs-string">" must retain the same host and port"</span>)<span class="hljs-comment">;</span> } if(this<span class="hljs-preprocessor">.calls</span> > <span class="hljs-number">1</span>) { throw new IllegalStateException(<span class="hljs-string">"network interceptor "</span> + response + <span class="hljs-string">" must call proceed() exactly once"</span>)<span class="hljs-comment">;</span> } } if(this<span class="hljs-preprocessor">.index</span> < HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.client</span><span class="hljs-preprocessor">.networkInterceptors</span>()<span class="hljs-preprocessor">.size</span>()) { //根据拦截器的数目去相应取出拦截器并执行intercept里面用户自定义的处理方式 HttpEngine<span class="hljs-preprocessor">.NetworkInterceptorChain</span> var7 = HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.new</span> NetworkInterceptorChain(this<span class="hljs-preprocessor">.index</span> + <span class="hljs-number">1</span>, request)<span class="hljs-comment">;</span> Interceptor var10 = (Interceptor)HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.client</span><span class="hljs-preprocessor">.networkInterceptors</span>()<span class="hljs-preprocessor">.get</span>(this<span class="hljs-preprocessor">.index</span>)<span class="hljs-comment">;</span> Response interceptedResponse = var10<span class="hljs-preprocessor">.intercept</span>(var7)<span class="hljs-comment">;</span> if(var7<span class="hljs-preprocessor">.calls</span> != <span class="hljs-number">1</span>) { throw new IllegalStateException(<span class="hljs-string">"network interceptor "</span> + var10 + <span class="hljs-string">" must call proceed() exactly once"</span>)<span class="hljs-comment">;</span> } else { return interceptedResponse<span class="hljs-comment">;</span> } } else { //写入请求头部 HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.transport</span><span class="hljs-preprocessor">.writeRequestHeaders</span>(request)<span class="hljs-comment">;</span> HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.networkRequest</span> = request<span class="hljs-comment">;</span> if(HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.permitsRequestBody</span>() && request<span class="hljs-preprocessor">.body</span>() != null) { //写入一些请求体 Sink var5 = HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.transport</span><span class="hljs-preprocessor">.createRequestBody</span>(request, request<span class="hljs-preprocessor">.body</span>()<span class="hljs-preprocessor">.contentLength</span>())<span class="hljs-comment">;</span> BufferedSink var8 = Okio<span class="hljs-preprocessor">.buffer</span>(var5)<span class="hljs-comment">;</span> request<span class="hljs-preprocessor">.body</span>()<span class="hljs-preprocessor">.writeTo</span>(var8)<span class="hljs-comment">;</span> var8<span class="hljs-preprocessor">.close</span>()<span class="hljs-comment">;</span> } //将之前写入的数据flush给socket并读取服务器答复 Response var6 = HttpEngine<span class="hljs-preprocessor">.this</span><span class="hljs-preprocessor">.readNetworkResponse</span>()<span class="hljs-comment">;</span> int var9 = var6<span class="hljs-preprocessor">.code</span>()<span class="hljs-comment">;</span> if((var9 == <span class="hljs-number">204</span> || var9 == <span class="hljs-number">205</span>) && var6<span class="hljs-preprocessor">.body</span>()<span class="hljs-preprocessor">.contentLength</span>() > <span class="hljs-number">0</span>L) { throw new ProtocolException(<span class="hljs-string">"HTTP "</span> + var9 + <span class="hljs-string">" had non-zero Content-Length: "</span> + var6<span class="hljs-preprocessor">.body</span>()<span class="hljs-preprocessor">.contentLength</span>())<span class="hljs-comment">;</span> } else { return var6<span class="hljs-comment">;</span> } } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li></ul>
我们先来看看27行的头部写入是怎么一个写法
<code class="hljs avrasm has-numbering">public void writeRequestHeaders(Request request) throws IOException { this<span class="hljs-preprocessor">.httpEngine</span><span class="hljs-preprocessor">.writingRequestHeaders</span>()<span class="hljs-comment">;</span> //组装请求的信息,比如url,请求方式,请求协议 String requestLine = RequestLine<span class="hljs-preprocessor">.get</span>(request, this<span class="hljs-preprocessor">.httpEngine</span><span class="hljs-preprocessor">.getConnection</span>()<span class="hljs-preprocessor">.getRoute</span>()<span class="hljs-preprocessor">.getProxy</span>()<span class="hljs-preprocessor">.type</span>(), this<span class="hljs-preprocessor">.httpEngine</span><span class="hljs-preprocessor">.getConnection</span>()<span class="hljs-preprocessor">.getProtocol</span>())<span class="hljs-comment">;</span> //将请求头和请求体写入socket this<span class="hljs-preprocessor">.httpConnection</span><span class="hljs-preprocessor">.writeRequest</span>(request<span class="hljs-preprocessor">.headers</span>(), requestLine)<span class="hljs-comment">;</span> }public void writeRequest(Headers headers, String requestLine) throws IOException { if(this<span class="hljs-preprocessor">.state</span> != <span class="hljs-number">0</span>) { throw new IllegalStateException(<span class="hljs-string">"state: "</span> + this<span class="hljs-preprocessor">.state</span>)<span class="hljs-comment">;</span> } else { this<span class="hljs-preprocessor">.sink</span><span class="hljs-preprocessor">.writeUtf</span>8(requestLine)<span class="hljs-preprocessor">.writeUtf</span>8(<span class="hljs-string">"\r\n"</span>)<span class="hljs-comment">;</span> int i = <span class="hljs-number">0</span><span class="hljs-comment">;</span> for(int size = headers<span class="hljs-preprocessor">.size</span>()<span class="hljs-comment">; i < size; ++i) {</span> this<span class="hljs-preprocessor">.sink</span><span class="hljs-preprocessor">.writeUtf</span>8(headers<span class="hljs-preprocessor">.name</span>(i))<span class="hljs-preprocessor">.writeUtf</span>8(<span class="hljs-string">": "</span>)<span class="hljs-preprocessor">.writeUtf</span>8(headers<span class="hljs-preprocessor">.value</span>(i))<span class="hljs-preprocessor">.writeUtf</span>8(<span class="hljs-string">"\r\n"</span>)<span class="hljs-comment">;</span> } this<span class="hljs-preprocessor">.sink</span><span class="hljs-preprocessor">.writeUtf</span>8(<span class="hljs-string">"\r\n"</span>)<span class="hljs-comment">;</span> this<span class="hljs-preprocessor">.state</span> = <span class="hljs-number">1</span><span class="hljs-comment">;</span> } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li></ul>
上面的sink就是socket的写入流,在上篇文章我们分析过怎么得到它的,将请求头部和请求体写入socket后,proceed的第37行就要进行flush操作了
<code class="hljs avrasm has-numbering">private Response readNetworkResponse() throws IOException { //执行flush操作 this<span class="hljs-preprocessor">.transport</span><span class="hljs-preprocessor">.finishRequest</span>()<span class="hljs-comment">;</span> //等待服务器相应并读取服务器返回信息组装成我们需要的response Response networkResponse = this<span class="hljs-preprocessor">.transport</span><span class="hljs-preprocessor">.readResponseHeaders</span>()<span class="hljs-preprocessor">.request</span>(this<span class="hljs-preprocessor">.networkRequest</span>)<span class="hljs-preprocessor">.handshake</span>(this<span class="hljs-preprocessor">.connection</span><span class="hljs-preprocessor">.getHandshake</span>())<span class="hljs-preprocessor">.header</span>(OkHeaders<span class="hljs-preprocessor">.SENT</span>_MILLIS, Long<span class="hljs-preprocessor">.toString</span>(this<span class="hljs-preprocessor">.sentRequestMillis</span>))<span class="hljs-preprocessor">.header</span>(OkHeaders<span class="hljs-preprocessor">.RECEIVED</span>_MILLIS, Long<span class="hljs-preprocessor">.toString</span>(System<span class="hljs-preprocessor">.currentTimeMillis</span>()))<span class="hljs-preprocessor">.build</span>()<span class="hljs-comment">;</span> if(!this<span class="hljs-preprocessor">.forWebSocket</span>) { //组装response的body networkResponse = networkResponse<span class="hljs-preprocessor">.newBuilder</span>()<span class="hljs-preprocessor">.body</span>(this<span class="hljs-preprocessor">.transport</span><span class="hljs-preprocessor">.openResponseBody</span>(networkResponse))<span class="hljs-preprocessor">.build</span>()<span class="hljs-comment">;</span> } Internal<span class="hljs-preprocessor">.instance</span><span class="hljs-preprocessor">.setProtocol</span>(this<span class="hljs-preprocessor">.connection</span>, networkResponse<span class="hljs-preprocessor">.protocol</span>())<span class="hljs-comment">;</span> return networkResponse<span class="hljs-comment">;</span> }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>
先简单看下第三行finishRequest调用的代码
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">finishRequest</span>() <span class="hljs-keyword">throws</span> IOException { <span class="hljs-keyword">this</span>.httpConnection.flush(); }<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">flush</span>() <span class="hljs-keyword">throws</span> IOException { <span class="hljs-keyword">this</span>.sink.flush(); }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>
再来看看第5行的组装步骤
<code class="hljs avrasm has-numbering">public Builder readResponseHeaders() throws IOException { return this<span class="hljs-preprocessor">.httpConnection</span><span class="hljs-preprocessor">.readResponse</span>()<span class="hljs-comment">;</span> }public Builder readResponse() throws IOException { if(this<span class="hljs-preprocessor">.state</span> != <span class="hljs-number">1</span> && this<span class="hljs-preprocessor">.state</span> != <span class="hljs-number">3</span>) { throw new IllegalStateException(<span class="hljs-string">"state: "</span> + this<span class="hljs-preprocessor">.state</span>)<span class="hljs-comment">;</span> } else { try { StatusLine e<span class="hljs-comment">;</span> Builder exception1<span class="hljs-comment">;</span> do { //从输入流里读出答复并组装成答复消息 e = StatusLine<span class="hljs-preprocessor">.parse</span>(this<span class="hljs-preprocessor">.source</span><span class="hljs-preprocessor">.readUtf</span>8LineStrict())<span class="hljs-comment">;</span> exception1 = (new Builder())<span class="hljs-preprocessor">.protocol</span>(e<span class="hljs-preprocessor">.protocol</span>)<span class="hljs-preprocessor">.code</span>(e<span class="hljs-preprocessor">.code</span>)<span class="hljs-preprocessor">.message</span>(e<span class="hljs-preprocessor">.message</span>)<span class="hljs-comment">;</span> <span class="hljs-keyword">com</span><span class="hljs-preprocessor">.squareup</span><span class="hljs-preprocessor">.okhttp</span><span class="hljs-preprocessor">.Headers</span><span class="hljs-preprocessor">.Builder</span> headersBuilder = new <span class="hljs-keyword">com</span><span class="hljs-preprocessor">.squareup</span><span class="hljs-preprocessor">.okhttp</span><span class="hljs-preprocessor">.Headers</span><span class="hljs-preprocessor">.Builder</span>()<span class="hljs-comment">;</span> //答复头部的读取 this<span class="hljs-preprocessor">.readHeaders</span>(headersBuilder)<span class="hljs-comment">;</span> headersBuilder<span class="hljs-preprocessor">.add</span>(OkHeaders<span class="hljs-preprocessor">.SELECTED</span>_PROTOCOL, e<span class="hljs-preprocessor">.protocol</span><span class="hljs-preprocessor">.toString</span>())<span class="hljs-comment">;</span> exception1<span class="hljs-preprocessor">.headers</span>(headersBuilder<span class="hljs-preprocessor">.build</span>())<span class="hljs-comment">;</span> } while(e<span class="hljs-preprocessor">.code</span> == <span class="hljs-number">100</span>)<span class="hljs-comment">;</span> this<span class="hljs-preprocessor">.state</span> = <span class="hljs-number">4</span><span class="hljs-comment">;</span> return exception1<span class="hljs-comment">;</span> } catch (EOFException var4) { IOException exception = new IOException(<span class="hljs-string">"unexpected end of stream on "</span> + this<span class="hljs-preprocessor">.connection</span> + <span class="hljs-string">" (recycle count="</span> + Internal<span class="hljs-preprocessor">.instance</span><span class="hljs-preprocessor">.recycleCount</span>(this<span class="hljs-preprocessor">.connection</span>) + <span class="hljs-string">")"</span>)<span class="hljs-comment">;</span> exception<span class="hljs-preprocessor">.initCause</span>(var4)<span class="hljs-comment">;</span> throw exception<span class="hljs-comment">;</span> } } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li></ul>
上面的代码我们看到的是对答复头部的读取整理,而readNetworkResponse()第8行则是对服务器答复的body进行整理组装
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> ResponseBody <span class="hljs-title">openResponseBody</span>(Response response) <span class="hljs-keyword">throws</span> IOException { Source source = <span class="hljs-keyword">this</span>.getTransferStream(response); <span class="hljs-comment">//最终返回一个输入流</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RealResponseBody(response.headers(), Okio.buffer(source)); } <span class="hljs-keyword">private</span> Source <span class="hljs-title">getTransferStream</span>(Response response) <span class="hljs-keyword">throws</span> IOException { <span class="hljs-keyword">if</span>(!HttpEngine.hasBody(response)) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.httpConnection.newFixedLengthSource(<span class="hljs-number">0</span>L); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(<span class="hljs-string">"chunked"</span>.equalsIgnoreCase(response.header(<span class="hljs-string">"Transfer-Encoding"</span>))) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.httpConnection.newChunkedSource(<span class="hljs-keyword">this</span>.httpEngine); } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">long</span> contentLength = OkHeaders.contentLength(response); <span class="hljs-keyword">return</span> contentLength != -<span class="hljs-number">1</span>L?<span class="hljs-keyword">this</span>.httpConnection.newFixedLengthSource(contentLength):<span class="hljs-keyword">this</span>.httpConnection.newUnknownLengthSource(); } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>
在经过这么层层代码的深入(好多代码,看得都乱了吧),我们最终得到了服务器返回的userResponse。。
最后的最后,我们要回到OKHttp源码解析(一) 最后一段代码getResponse的52行得到我们上面分析的userResponse,并关闭相应的连接池,关闭回收socket等”善后“处理。
三篇文章分析到现在,算是把整个OKHttp的流程分析了个大概,OkHttp还有其他的一些部分,比如handshake,连接池的管理等方面的内容,如果有人有发现这方面介绍的好文章可以留言推荐给我看看,大家共同学习,共同进步。
0 0
- OKHttp源码解析(三)
- OKHttp源码解析(三)
- OKHttp源码解析(三)
- Android OkHttp(三)源码解析
- OkHttp完全解析(十)源码解析三
- 安卓面试清单----OKHttp源码解析(三)
- OKHttp源码解析(一)
- OKHttp源码解析(二)
- OKHttp源码解析(二)
- OKHttp源码解析(二)
- OKHttp源码解析(一)
- OKHttp源码解析(一)
- OkHttp源码解析(1)
- okhttp源码解析(2)
- okhttp源码解析(3)
- OKHttp源码解析(一)
- OKHttp源码解析(二)
- OkHttp源码解析(一)
- Monkey
- Hive自身数据导出的方式
- HttpClient使用详解
- 解决重写的TextView跑马灯一直跑的问题
- ACM-会场安排问题
- OKHttp源码解析(三)
- UIViewController中edgesForExtendedLayout属性的画面切断现象
- 高性能网站建设之 MS Sql Server数据库分区
- iOS静态库动态库基本使用
- HTTP必知必会
- OKHttp源码解析(二)
- 基于 HTTP 长连接的“服务器推”技术
- Lucene4.10.4实践 索引联合查询数据库实现查询更快
- webbench源码分析(转)