如何用Httpurlconnection header获取Header文件的内容

node.js+Android(使用HttpURLConnection和HttpClient)实现文件上传_Linux编程_Linux公社-Linux系统门户网站
你好,游客
node.js+Android(使用HttpURLConnection和HttpClient)实现文件上传
来源:Linux社区&
作者:dead-horse
这篇node.js 第三方模块如何安装(使用npm)及介绍& 了formidable的安装(见),这一篇结合写一个文件上传的例子。
先上服务端node.js 的代码,保存为upload.js
[javascript]
var&http&=&require('http');&&
var&fs&=&require('fs');&&
var&formidable&=&require('formidable');&&
var&firstPage&=&function(res){&&
&&&&res.writeHead(200,&{'Content-Type':&'text/html'});&&
&&&&var&html&=&fs.readFileSync(__dirname&+&'/public/form.html');&&
&&&&res.end(html);&&
var&resultPage&=&function(res,data,files){&&
&&&&res.setHeader('Content-Type',&'text/html');&&
&&&&res.write('&p&thanks&'&+&data.name&+&'&/p&');&&
&&&&res.write('&ul&');&&
&&&&console.log(data);&&
&&&&console.log(files);&&
&&&&if&(Array.isArray(files.images))&{&&
&&&&&&files.images.forEach(function(image){&&
&&&&&&&&var&kb&=&image.size&/&1024&|&0;&&
&&&&&&&&res.write('&li&uploaded&'&+&image.name&+&'&'&+&kb&+&'kb&/li&');&&
&&&&&&});&&
&&&&}&else&{&&
&&&&&&var&image&=&files.&&
&&&&&&var&kb&=&image.size&/&1024&|&0;&&
&&&&&&res.write('&li&uploaded&'&+&image.name&+&'&'&+&kb&+&'kb&/li&');&&
&&&&res.end('&/ul&');&&
var&server&=&http.createServer(function(req,&res)&{&&
&&&&if&(req.method&==&'GET'){&&
&&&&&&&&return&firstPage(res);&&
&&&&var&form&=&new&formidable.IncomingF&&
&&&&var&data&=&{};&&
&&&&var&files&=&{};&&
&&&&form.uploadDir&=&__dirname&+'/file';&&
&&&&form.keepExtensions&=&true;&&
&&&&function&ondata(name,&val,&data){&&
&&&&&&&&if&(Array.isArray(data[name]))&{&&
&&&&&&&&&&&&data[name].push(val);&&
&&&&&&&&}&else&if&(data[name])&{&&
&&&&&&&&&&&&data[name]&=&[data[name],&val];&&
&&&&&&&&}&else&{&&
&&&&&&&&&&&&data[name]&=&&&
&&&&&&&&}&&
&&&&form.on('field',&function(name,&val){&&
&&&&&&&&ondata(name,&val,&data);&&
&&&&form.on('file',&function(name,&val){&&
&&&&&&&&ondata(name,&val,&files);&&
&&&&form.on('end',&function()&{&&
&&&&&&&&resultPage(res,data,files);&&&
&&&&form.parse(req);&&
server.listen(8080);&&
console.log('listening&on&http://localhost:8080');&&__dirname + '/public/form.html,在js当前目录下建立public文件夹,文件夹下建立form.html文件,文件内容如下
&&&&&&&&&action="/"&method="post"&enctype="multipart/form-data"&&
&&&&&&&&&&&type="text"&name="name"&placeholder="Name:"&&&
&&&&&&&&&&&type="file"&name="images"&multiple="multiple"&&&
&&&&&&&&&&&type="submit"&value="Upload"&&&
&&&&&&&&&&
代码比较简单,formidable部分可以上官网看API。
运行以下看看
在浏览器中打开http://localhost:8080
选择文件,输入文件name
点击upload
文件上传成功
再看android代码,布局文件main.xml如下
&version="1.0"&encoding="utf-8"&&
&xmlns:android="/apk/res/android"&&
&&&&android:id="@+id/RelativeLayout1"&&
&&&&android:layout_width="fill_parent"&&
&&&&android:layout_height="fill_parent"&&
&&&&android:orientation="vertical"&&&
&&&&&&&&android:id="@+id/textViewInfo"&&
&&&&&&&&android:layout_width="fill_parent"&&
&&&&&&&&android:layout_height="wrap_content"&&
&&&&&&&&android:layout_alignParentLeft="true"&&
&&&&&&&&android:layout_alignParentTop="true"&&
&&&&&&&&&&
&&&&&&&&android:id="@+id/textView1"&&
&&&&&&&&android:layout_width="wrap_content"&&
&&&&&&&&android:layout_height="wrap_content"&&
&&&&&&&&android:layout_alignParentLeft="true"&&
&&&&&&&&android:layout_below="@+id/textViewInfo"&&
&&&&&&&&android:layout_marginLeft="40dp"&&
&&&&&&&&android:layout_marginTop="20dp"&&
&&&&&&&&android:text="文件路径"&&&
&&&&&&&&android:textAppearance="?android:attr/textAppearanceMedium"&&&
&&&&&&&&android:id="@+id/textViewFile"&&
&&&&&&&&android:layout_width="wrap_content"&&
&&&&&&&&android:layout_height="wrap_content"&&
&&&&&&&&android:layout_alignLeft="@+id/textView1"&&
&&&&&&&&android:layout_below="@+id/textView1"&&
&&&&&&&&android:id="@+id/textView3"&&
&&&&&&&&android:layout_width="wrap_content"&&
&&&&&&&&android:layout_height="wrap_content"&&
&&&&&&&&android:layout_alignLeft="@+id/textViewFile"&&
&&&&&&&&android:layout_below="@+id/textViewFile"&&
&&&&&&&&android:layout_marginTop="10dp"&&
&&&&&&&&android:text="上传网址"&&&
&&&&&&&&android:textAppearance="?android:attr/textAppearanceMedium"&&&
&&&&&&&&android:id="@+id/textViewUrl"&&
&&&&&&&&android:layout_width="wrap_content"&&
&&&&&&&&android:layout_height="wrap_content"&&
&&&&&&&&android:layout_alignLeft="@+id/textView3"&&
&&&&&&&&android:layout_below="@+id/textView3"&&
&&&&&&&&android:id="@+id/webViewResult"&&
&&&&&&&&android:layout_width="fill_parent"&&
&&&&&&&&android:layout_height="100dp"&&
&&&&&&&&android:layout_below="@+id/textViewUrl"&&
&&&&&&&&android:layout_marginTop="30dp"&&
&&&&&&&&android:id="@+id/buttonJava"&&
&&&&&&&&android:layout_width="wrap_content"&&
&&&&&&&&android:layout_height="wrap_content"&&
&&&&&&&&android:layout_alignParentBottom="true"&&
&&&&&&&&android:layout_marginLeft="60dp"&&
&&&&&&&&android:text="Java上传"&&&
&&&&&&&&android:id="@+id/buttonApache"&&
&&&&&&&&android:layout_width="wrap_content"&&
&&&&&&&&android:layout_height="wrap_content"&&
&&&&&&&&android:layout_alignBottom="@+id/buttonJava"&&
&&&&&&&&android:layout_toRightOf="@+id/buttonJava"&&
&&&&&&&&android:text="Apache上传"&&&
相关资讯 & & &
& (04月27日)
& (03月30日)
& (05月17日)
& (04月22日)
& (01月21日)
图片资讯 & & &
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款下次自动登录
关注移动互联网和移动APP开发工具、开发框架、测试工具、微信开发、Android源码、Android开源类库以及各种开源组件的IT科技网站
现在的位置:
Android 4.4以上使用HttpURLConnection底层使用OkHttp实现的源码分析
研究了一下HttpURLConnection的源码:
& &&在使用的时候都是通过URL.openConnection()来获取HttpURLConnection对象,然后调用其connect方法进行链接,所以先从URL.penConnection()入手:
* Returns a new connection to the resource referred to by this URL.
* IOException if an error occurs while opening the connection.
public URLConnection openConnection() throws IOException {
return streamHandler.openConnection(this);
接下来就要看一下streamHandler究竟是何方神圣?我们搜一下他的赋值,实在setupStreamHandler方法中进行的:
* Sets the receiver's stream handler to one which is appropriate for its
* protocol.
* &p&Note that this will overwrite any existing stream handler with the new
* one. Senders must check if the streamHandler is null before calling the
* method if they do not want this behavior (a speed optimization).
* MalformedURLException if no reasonable handler is available.
void setupStreamHandler() {
streamHandler = streamHandlers.get(protocol);
if (streamHandler != null) {
if (streamHandlerFactory != null) {
streamHandler = streamHandlerFactory.createURLStreamHandler(protocol);
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
String packageList = System.getProperty(&java.protocol.handler.pkgs&);
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (packageList != null && contextClassLoader != null) {
for (String packageName : packageList.split(&\\|&)) {
String className = packageName + &.& + protocol + &.Handler&;
Class&?& c = contextClassLoader.loadClass(className);
streamHandler = (URLStreamHandler) c.newInstance();
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
} catch (IllegalAccessException ignored) {
} catch (InstantiationException ignored) {
} catch (ClassNotFoundException ignored) {
if (protocol.equals(&file&)) {
streamHandler = new FileHandler();
} else if (protocol.equals(&ftp&)) {
streamHandler = new FtpHandler();
} else if (protocol.equals(&http&)) {
String name = &com.android.okhttp.HttpHandler&;
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
} else if (protocol.equals(&https&)) {
String name = &com.android.okhttp.HttpsHandler&;
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
} else if (protocol.equals(&jar&)) {
streamHandler = new JarHandler();
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
这里我们就以HTTP协议为例说一下所以找到okhttp
HttpHandler.openConnection()方法:
package com.squareup.
import java.io.IOE
import java.net.P
import java.net.URL;
import java.net.URLC
import java.net.URLStreamH
public final class HttpHandler extends URLStreamHandler {
@Override protected URLConnection openConnection(URL url) throws IOException {
return new OkHttpClient().open(url);
@Override protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {
if (url == null || proxy == null) {
throw new IllegalArgumentException(&url == null || proxy == null&);
return new OkHttpClient().setProxy(proxy).open(url);
@Override protected int getDefaultPort() {
return 80;
接下来就悲剧了,因为我找不到OkHttpClient()类中有open方法。&
仔细查看了文档后发现在OKHttp1.6.0的时候该方法就已经已经过时了。
@Deprecated
public HttpURLConnection open(URL url)
Deprecated. moved to OkUrlFactory.open.
那我们怎么往下分析呢?很显然Android sdk中使用的OkHttp不是最新版。所以我们可以使用1.5.0版本的OKHttp接着分析。&
在项目build.gradle中进行配置 compile ‘com.squareup.okhttp:okhttp:1.5.0’ 然后开始愉快的查看源码。
package com.squareup.
import com.squareup.okhttp.internal.U
import com.squareup.okhttp.internal.http.HttpA
import com.squareup.okhttp.internal.http.HttpURLConnectionI
import com.squareup.okhttp.internal.http.HttpsURLConnectionI
import com.squareup.okhttp.internal.http.ResponseCacheA
import com.squareup.okhttp.internal.okio.ByteS
import com.squareup.okhttp.internal.tls.OkHostnameV
import java.io.IOE
import java.net.CookieH
import java.net.HttpURLC
import java.net.P
import java.net.ProxyS
import java.net.ResponseC
import java.net.URL;
import java.net.URLC
import java.net.URLStreamH
import java.net.URLStreamHandlerF
import java.security.GeneralSecurityE
import java.util.ArrayL
import java.util.L
import java.util.concurrent.TimeU
import javax.net.ssl.HostnameV
import javax.net.ssl.SSLC
import javax.net.ssl.SSLSocketF
/** Configures and creates HTTP connections. */
public final class OkHttpClient implements URLStreamHandlerFactory, Cloneable {
private final RouteDatabase routeD
private List&Protocol&
private ProxySelector proxyS
private CookieHandler cookieH
private OkResponseCache responseC
private SSLSocketFactory sslSocketF
private HostnameVerifier hostnameV
private OkAuthent
private ConnectionPool connectionP
private boolean followProtocolRedirects = true;
private int connectT
private int readT
public OkHttpClient() {
routeDatabase = new RouteDatabase();
* Sets the default connect timeout for new connections. A value of 0 means no timeout.
* URLConnection#setConnectTimeout(int)
public void setConnectTimeout(long timeout, TimeUnit unit) {
if (timeout & 0) {
throw new IllegalArgumentException(&timeout & 0&);
if (unit == null) {
throw new IllegalArgumentException(&unit == null&);
long millis = unit.toMillis(timeout);
if (millis & Integer.MAX_VALUE) {
throw new IllegalArgumentException(&Timeout too large.&);
connectTimeout = (int)
/** Default connect timeout (in milliseconds). */
public int getConnectTimeout() {
return connectT
* Sets the default read timeout for new connections. A value of 0 means no timeout.
* URLConnection#setReadTimeout(int)
public void setReadTimeout(long timeout, TimeUnit unit) {
if (timeout & 0) {
throw new IllegalArgumentException(&timeout & 0&);
if (unit == null) {
throw new IllegalArgumentException(&unit == null&);
long millis = unit.toMillis(timeout);
if (millis & Integer.MAX_VALUE) {
throw new IllegalArgumentException(&Timeout too large.&);
readTimeout = (int)
/** Default read timeout (in milliseconds). */
public int getReadTimeout() {
return readT
* Sets the HTTP proxy that will be used by connections created by this
* client. This takes precedence over
#setProxySelector}, which is
* only honored when this proxy is null (which it is by default). To disable
* proxy use completely, call
setProxy(Proxy.NO_PROXY)}.
public OkHttpClient setProxy(Proxy proxy) {
this.proxy =
return this;
public Proxy getProxy() {
* Sets the proxy selection policy to be used if no
#setProxy proxy}
* is specified explicitly. The proxy selector may ret
* in that case they will be tried in sequence until a successful connection
* is established.
* &p&If unset, the
ProxySelector#getDefault() system-wide default}
* proxy selector will be used.
public OkHttpClient setProxySelector(ProxySelector proxySelector) {
this.proxySelector = proxyS
return this;
public ProxySelector getProxySelector() {
return proxyS
* Sets the cookie handler to be used to read outgoing cookies and write
* incoming cookies.
* &p&If unset, the
CookieHandler#getDefault() system-wide default}
* cookie handler will be used.
public OkHttpClient setCookieHandler(CookieHandler cookieHandler) {
this.cookieHandler = cookieH
return this;
public CookieHandler getCookieHandler() {
return cookieH
* Sets the response cache to be used to read and write cached responses.
public OkHttpClient setResponseCache(ResponseCache responseCache) {
return setOkResponseCache(toOkResponseCache(responseCache));
public ResponseCache getResponseCache() {
return responseCache instanceof ResponseCacheAdapter
? ((ResponseCacheAdapter) responseCache).getDelegate()
public OkHttpClient setOkResponseCache(OkResponseCache responseCache) {
this.responseCache = responseC
return this;
public OkResponseCache getOkResponseCache() {
return responseC
* Sets the socket factory used to secure HTTPS connections.
* &p&If unset, a lazily created SSL socket factory will be used.
public OkHttpClient setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketF
return this;
public SSLSocketFactory getSslSocketFactory() {
return sslSocketF
* Sets the verifier used to confirm that response certificates apply to
* requested hostnames for HTTPS connections.
* &p&If unset, the
javax.net.ssl.HttpsURLConnection#getDefaultHostnameVerifier()
* system-wide default} hostname verifier will be used.
public OkHttpClient setHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameV
return this;
public HostnameVerifier getHostnameVerifier() {
return hostnameV
* Sets the authenticator used to respond to challenges from the remote web
* server or proxy server.
* &p&If unset, the
java.net.Authenticator#setDefault system-wide default}
* authenticator will be used.
public OkHttpClient setAuthenticator(OkAuthenticator authenticator) {
this.authenticator =
return this;
public OkAuthenticator getAuthenticator() {
* Sets the connection pool used to recycle HTTP and HTTPS connections.
* &p&If unset, the
ConnectionPool#getDefault() system-wide
* default} connection pool will be used.
public OkHttpClient setConnectionPool(ConnectionPool connectionPool) {
this.connectionPool = connectionP
return this;
public ConnectionPool getConnectionPool() {
return connectionP
* Configure this client to follow redirects from HTTPS to HTTP and from HTTP
* to HTTPS.
* &p&If unset, protocol redirects will be followed. This is different than
* the built-in
HttpURLConnection}'s default.
public OkHttpClient setFollowProtocolRedirects(boolean followProtocolRedirects) {
this.followProtocolRedirects = followProtocolR
return this;
public boolean getFollowProtocolRedirects() {
return followProtocolR
public RouteDatabase getRoutesDatabase() {
return routeD
* OkHttp 1.5 enforces an enumeration of
Protocol protocols}
* that can be selected. Please switch to
#setProtocols(java.util.List)}.
@Deprecated
public OkHttpClient setTransports(List&String& transports) {
List&Protocol& protocols = new ArrayList&Protocol&(transports.size());
for (int i = 0, size = transports.size(); i & i++) {
Protocol protocol = Util.getProtocol(ByteString.encodeUtf8(transports.get(i)));
protocols.add(protocol);
} catch (IOException e) {
throw new IllegalArgumentException(e);
return setProtocols(protocols);
* Configure the protocols used by this client to communicate with remote
* servers. By default this client will prefer the most efficient transport
* available, falling back to more ubiquitous protocols. Applications should
* only call this method to avoid specific compatibility problems, such as web
* servers that behave incorrectly when SPDY is enabled.
* &p&The following protocols are currently supported:
* &li&&a href=&http://www.w3.org/Protocols/rfc2616/rfc2616.html&&http/1.1&/a&
* &li&&a href=&http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1&&spdy/3.1&/a&
* &li&&a href=&http://tools.ietf.org/html/draft-ietf-httpbis-http2-09&&HTTP-draft-09/2.0&/a&
* &p&&strong&This is an evolving set.&/strong& Future releases may drop
* support for transitional protocols (like spdy/3.1), in favor of their
* successors (spdy/4 or http/2.0). The http/1.1 transport will never be
* dropped.
* &p&If multiple protocols are specified, &a
* href=&/git/nextprotoneg.html&&NPN&/a& will
* be used to negotiate a transport. Future releases may use another mechanism
* (such as &a href=&http://tools.ietf.org/html/draft-friedl-tls-applayerprotoneg-02&&ALPN&/a&)
* to negotiate a transport.
* protocols the protocols to use, in order of preference. The list
* must contain &http/1.1&. It must not contain null.
public OkHttpClient setProtocols(List&Protocol& protocols) {
protocols = Util.immutableList(protocols);
if (!protocols.contains(Protocol.HTTP_11)) {
throw new IllegalArgumentException(&protocols doesn't contain http/1.1: & + protocols);
if (protocols.contains(null)) {
throw new IllegalArgumentException(&protocols must not contain null&);
this.protocols = Util.immutableList(protocols);
return this;
* OkHttp 1.5 enforces an enumeration of
* protocols} that can be selected. Please switch to
* #getProtocols()}.
@Deprecated
public List&String& getTransports() {
List&String& transports = new ArrayList&String&(protocols.size());
for (int i = 0, size = protocols.size(); i & i++) {
transports.add(protocols.get(i).name.utf8());
public List&Protocol& getProtocols() {
public HttpURLConnection open(URL url) {
return open(url, proxy);
HttpURLConnection open(URL url, Proxy proxy) {
String protocol = url.getProtocol();
OkHttpClient copy = copyWithDefaults();
copy.proxy =
if (protocol.equals(&http&)) return new HttpURLConnectionImpl(url, copy);
if (protocol.equals(&https&)) return new HttpsURLConnectionImpl(url, copy);
throw new IllegalArgumentException(&Unexpected protocol: & + protocol);
* Returns a shallow copy of this OkHttpClient that uses the system-wide
* default for each field that hasn't been explicitly configured.
OkHttpClient copyWithDefaults() {
OkHttpClient result = clone();
if (result.proxySelector == null) {
result.proxySelector = ProxySelector.getDefault();
if (result.cookieHandler == null) {
result.cookieHandler = CookieHandler.getDefault();
if (result.responseCache == null) {
result.responseCache = toOkResponseCache(ResponseCache.getDefault());
if (result.sslSocketFactory == null) {
result.sslSocketFactory = getDefaultSSLSocketFactory();
if (result.hostnameVerifier == null) {
result.hostnameVerifier = OkHostnameVerifier.INSTANCE;
if (result.authenticator == null) {
result.authenticator = HttpAuthenticator.SYSTEM_DEFAULT;
if (result.connectionPool == null) {
result.connectionPool = ConnectionPool.getDefault();
if (result.protocols == null) {
result.protocols = Util.HTTP2_SPDY3_AND_HTTP;
* Java and Android programs default to using a single global SSL context,
* accessible to HTTP clients as
SSLSocketFactory#getDefault()}. If we
* used the shared SSL context, when OkHttp enables NPN for its SPDY-related
* stuff, it would also enable NPN for other usages, which might crash them
* because NPN is enabled when it isn't expected to be.
* This code avoids that by defaulting to an OkHttp created SSL context. The
* significant drawback of this approach is that apps that customize the
* global SSL context will lose these customizations.
private synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
if (sslSocketFactory == null) {
SSLContext sslContext = SSLContext.getInstance(&TLS&);
sslContext.init(null, null, null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new AssertionError();
return sslSocketF
/** Returns a shallow copy of this OkHttpClient. */
@Override public OkHttpClient clone() {
return (OkHttpClient) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
private OkResponseCache toOkResponseCache(ResponseCache responseCache) {
return responseCache == null || responseCache instanceof OkResponseCache
? (OkResponseCache) responseCache
: new ResponseCacheAdapter(responseCache);
* Creates a URLStreamHandler as a
URL#setURLStreamHandlerFactory}.
* &p&This code configures OkHttp to handle all HTTP and HTTPS connections
* created with
URL#openConnection()}: &pre&
* OkHttpClient okHttpClient = new OkHttpClient();
* URL.setURLStreamHandlerFactory(okHttpClient);
public URLStreamHandler createURLStreamHandler(final String protocol) {
if (!protocol.equals(&http&) && !protocol.equals(&https&)) return null;
return new URLStreamHandler() {
@Override protected URLConnection openConnection(URL url) {
return open(url);
@Override protected URLConnection openConnection(URL url, Proxy proxy) {
return open(url, proxy);
@Override protected int getDefaultPort() {
if (protocol.equals(&http&)) return 80;
if (protocol.equals(&https&)) return 443;
throw new AssertionError();
接着看一下HttpURLConnectionImpl类,它是HttpURLConnection的子类:
public class HttpURLConnectionImpl extends HttpURLConnection {
到这里new URL(url).openConnection()方法已经分析完了,其实就是返回了一个HtppURLConnectionImpl对象。
我们在使用HttpURLConnection都是这样使用:
String url = &&
URL url = new URL(url);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.connect();
connection.getResponseCode();
connection.getOuoputStream();
connection.getInputStream();
上面分析了new URL().openConnection()那我们这里就接着分析第二步了,就是调用connect()方法的处理:&
这里看一下HttpURLConnectionImpl.connect()方法:
@Override public final void connect() throws IOException {
initHttpEngine();
success = execute(false);
} while (!success);
接着看一下initHttpEngine()方法的实现:
private void initHttpEngine() throws IOException {
if (httpEngineFailure != null) {
throw httpEngineF
} else if (httpEngine != null) {
connected = true;
if (doOutput) {
if (method.equals(&GET&)) {
method = &POST&;
} else if (!HttpMethod.hasRequestBody(method)) {
throw new ProtocolException(method + & does not support writing&);
httpEngine = newHttpEngine(method, null, null);
} catch (IOException e) {
httpEngineFailure =
private HttpEngine newHttpEngine(String method, Connection connection,
RetryableSink requestBody) {
Request.Builder builder = new Request.Builder()
.url(getURL())
.method(method, null );
Headers headers = requestHeaders.build();
for (int i = 0; i & headers.size(); i++) {
builder.addHeader(headers.name(i), headers.value(i));
boolean bufferRequestB
if (fixedContentLength != -1) {
bufferRequestBody = false;
builder.header(&Content-Length&, Long.toString(fixedContentLength));
} else if (chunkLength & 0) {
bufferRequestBody = false;
builder.header(&Transfer-Encoding&, &chunked&);
bufferRequestBody = true;
Request request = builder.build();
OkHttpClient engineClient =
if (engineClient.getOkResponseCache() != null && !getUseCaches()) {
engineClient = client.clone().setOkResponseCache(null);
return new HttpEngine(engineClient, request, bufferRequestBody, connection, null, requestBody);
到这里我们知道他会创建一个HttpEngine类,我们先不管它,接着看一下execute()方法的内部实现:
* Sends a request and optionally reads a response. Returns true if the
* request was successfully executed, and false if the request can be
* retried. Throws an exception if the request failed permanently.
private boolean execute(boolean readResponse) throws IOException {
httpEngine.sendRequest();
route = httpEngine.getRoute();
handshake = httpEngine.getConnection() != null
? httpEngine.getConnection().getHandshake()
if (readResponse) {
httpEngine.readResponse();
return true;
} catch (IOException e) {
HttpEngine retryEngine = httpEngine.recover(e);
if (retryEngine != null) {
httpEngine = retryE
return false;
httpEngineFailure =
到这里,可以大胆的猜测一下了HttpEngine应该就是实际在Socket链接上进行数据收发的类。
当然这只是猜测,接着看一下它的实现:
* Handles a single HTTP request/response pair. Each HTTP engine follows this
* lifecycle:
* &li&It is created.
* &li&The HTTP request message is sent with sendRequest(). Once the request
* is sent it is an error to modify the request headers. After
* sendRequest() has been called the request body can be written to if
* it exists.
* &li&The HTTP response message is read with readResponse(). After the
* response has been read the response headers and body can be read.
* All responses have a response body input stream, though in some
* instances this stream is empty.
* &p&The request and response may be served by the HTTP response cache, by the
* network, or by both in the event of a conditional GET.
文档验证了我们的想法。因为它内部实现代码比较多,所以就不全部贴了,按照重要的一步步分析,既然上面调用了sendRequest()方法,这里就从他入手:   
* Figures out what the response source will be, and opens a socket to that
* source if necessary. Prepares the request headers and gets ready to start
* writing the request body if it exists.
public final void sendRequest() throws IOException {
if (responseSource != null) return;
if (transport != null) throw new IllegalStateException();
prepareRawRequestHeaders();
OkResponseCache responseCache = client.getOkResponseCache();
Response cacheResponse = responseCache != null
? responseCache.get(request)
long now = System.currentTimeMillis();
CacheStrategy cacheStrategy = new CacheStrategy.Factory(now, request, cacheResponse).get();
responseSource = cacheStrategy.
request = cacheStrategy.
if (responseCache != null) {
responseCache.trackResponse(responseSource);
if (responseSource != ResponseSource.NETWORK) {
validatingResponse = cacheStrategy.
if (cacheResponse != null && !responseSource.usesCache()) {
closeQuietly(cacheResponse.body());
if (responseSource.requiresConnection()) {
if (connection == null) {
connect();
transport = (Transport) connection.newTransport(this);
if (hasRequestBody() && requestBodyOut == null) {
requestBodyOut = transport.createRequestBody(request);
if (connection != null) {
client.getConnectionPool().recycle(connection);
connection = null;
this.response = validatingR
if (validatingResponse.body() != null) {
initContentStream(validatingResponse.body().source());
接着看一下connect()方法:
/** Connect to the origin server either directly or via a proxy. */
private void connect() throws IOException {
if (connection != null) throw new IllegalStateException();
if (routeSelector == null) {
String uriHost = request.url().getHost();
if (uriHost == null || uriHost.length() == 0) {
throw new UnknownHostException(request.url().toString());
SSLSocketFactory sslSocketFactory = null;
HostnameVerifier hostnameVerifier = null;
if (request.isHttps()) {
sslSocketFactory = client.getSslSocketFactory();
hostnameVerifier = client.getHostnameVerifier();
Address address = new Address(uriHost, getEffectivePort(request.url()), sslSocketFactory,
hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getProtocols());
routeSelector = new RouteSelector(address, request.uri(), client.getProxySelector(),
client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase());
connection = routeSelector.next(request.method());
if (!connection.isConnected()) {
connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig());
if (connection.isSpdy()) client.getConnectionPool().share(connection);
client.getRoutesDatabase().connected(connection.getRoute());
} else if (!connection.isSpdy()) {
connection.updateReadTimeout(client.getReadTimeout());
route = connection.getRoute();
接下来我们要先看一下routeSelector.next()方法如何返回connection对象,然后在看Connection.connect()方法:
* Returns the next route address to attempt.
* NoSuchElementException if there are no more routes to attempt.
public Connection next(String method) throws IOException {
for (C (pooled = pool.get(address)) != null; ) {
if (method.equals(&GET&) || pooled.isReadable()) return
pooled.close();
if (!hasNextTlsMode()) {
if (!hasNextInetSocketAddress()) {
if (!hasNextProxy()) {
if (!hasNextPostponed()) {
throw new NoSuchElementException();
return new Connection(pool, nextPostponed());
lastProxy = nextProxy();
resetNextInetSocketAddress(lastProxy);
lastInetSocketAddress = nextInetSocketAddress();
resetNextTlsMode();
boolean modernTls = nextTlsMode() == TLS_MODE_MODERN;
Route route = new Route(address, lastProxy, lastInetSocketAddress, modernTls);
if (routeDatabase.shouldPostpone(route)) {
postponedRoutes.add(route);
return next(method);
return new Connection(pool, route);
再看一下Connection类的实现以及其connect()方法:
public final class Connection implements Closeable {
private final ConnectionP
private final R
private InputS
private OutputS
private BufferedS
private BufferedS
private boolean connected = false;
private HttpConnection httpC
private SpdyConnection spdyC
private int httpMinorVersion = 1;
private long idleStartTimeNs;
private int recycleC
public Connection(ConnectionPool pool, Route route) {
this.pool =
this.route =
public void connect(int connectTimeout, int readTimeout, TunnelRequest tunnelRequest)
throws IOException {
if (connected) throw new IllegalStateException(&already connected&);
socket = (route.proxy.type() != Proxy.Type.HTTP) ? new Socket(route.proxy) : new Socket();
Platform.get().connectSocket(socket, route.inetSocketAddress, connectTimeout);
socket.setSoTimeout(readTimeout);
in = socket.getInputStream();
out = socket.getOutputStream();
if (route.address.sslSocketFactory != null) {
upgradeToTls(tunnelRequest);
initSourceAndSink();
httpConnection = new HttpConnection(pool, this, source, sink);
connected = true;
/** Returns the transport appropriate for this connection. */
public Object newTransport(HttpEngine httpEngine) throws IOException {
return (spdyConnection != null)
? new SpdyTransport(httpEngine, spdyConnection)
: new HttpTransport(httpEngine, httpConnection);
到这里就已经把发送请求到服务器的部分全部分析完了,就想上面所说的我们应该回去了,回去分析发送完请求后的部分。
我们这个分析是在`HttpURLConnectionImpl.execute()`方法中的`HttpEngine.sendRequest()`方法开始一直分析下来的,
所以我们还是要回到`HttpURLConnectionImpl.execute()`方法中.
&div class=&se-preview-section-delimiter&&&/div&
private boolean execute(boolean readResponse) throws IOException {
httpEngine.sendRequest();
route = httpEngine.getRoute();
handshake = httpEngine.getConnection() != null
? httpEngine.getConnection().getHandshake()
if (readResponse) {
httpEngine.readResponse();
return true;
} catch (IOException e) {
HttpEngine retryEngine = httpEngine.recover(e);
if (retryEngine != null) {
httpEngine = retryE
return false;
httpEngineFailure =
接着看HttpEngine.readResponse()方法吧。注释说的非常明白。
* Flushes the remaining request header and body, parses the HTTP response
* headers and starts reading the HTTP response body if it exists.
public final void readResponse() throws IOException {
if (response != null) return;
if (responseSource == null) throw new IllegalStateException(&call sendRequest() first!&);
if (!responseSource.requiresConnection()) return;
if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() & 0) {
bufferedRequestBody.flush();
if (sentRequestMillis == -1) {
if (OkHeaders.contentLength(request) == -1 && requestBodyOut instanceof RetryableSink) {
long contentLength = ((RetryableSink) requestBodyOut).contentLength();
request = request.newBuilder()
.header(&Content-Length&, Long.toString(contentLength))
transport.writeRequestHeaders(request);
if (requestBodyOut != null) {
if (bufferedRequestBody != null) {
bufferedRequestBody.close();
requestBodyOut.close();
if (requestBodyOut instanceof RetryableSink) {
transport.writeRequestBody((RetryableSink) requestBodyOut);
transport.flushRequest();
response = transport.readResponseHeaders()
.request(request)
.handshake(connection.getHandshake())
.header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
.header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
.setResponseSource(responseSource)
connection.setHttpMinorVersion(response.httpMinorVersion());
receiveHeaders(response.headers());
if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
if (validatingResponse.validate(response)) {
transport.emptyTransferStream();
releaseConnection();
response = combine(validatingResponse, response);
OkResponseCache responseCache = client.getOkResponseCache();
responseCache.trackConditionalCacheHit();
responseCache.update(validatingResponse, cacheableResponse());
if (validatingResponse.body() != null) {
initContentStream(validatingResponse.body().source());
closeQuietly(validatingResponse.body());
if (!hasResponseBody()) {
responseTransferSource = transport.getTransferStream(cacheRequest);
responseBody = responseTransferS
maybeCache();
initContentStream(transport.getTransferStream(cacheRequest));
先看一下maybeCache()方法:
private void maybeCache() throws IOException {
OkResponseCache responseCache = client.getOkResponseCache();
if (responseCache == null) return;
if (!CacheStrategy.isCacheable(response, request)) {
responseCache.maybeRemove(request);
cacheRequest = responseCache.put(cacheableResponse());
再看一下initContentStream()方法:
* Initialize the response content stream from the response transfer source.
* These two sources are the same unless we're doing transparent gzip, in
* which case the content source is decompressed.
* &p&Whenever we do transparent gzip we also strip the corresponding headers.
* We strip the Content-Encoding header to prevent the application from
* attempting to double decompress. We strip the Content-Length header because
* it is the length of the compressed content, but the application is only
* interested in the length of the uncompressed content.
* &p&This method should only be used for non-empty response bodies. Response
* codes like &304 Not Modified& can include &Content-Encoding: gzip& without
* a response body and we will crash if we attempt to decompress the zero-byte
private void initContentStream(Source transferSource) throws IOException {
responseTransferSource = transferS
if (transparentGzip && &gzip&.equalsIgnoreCase(response.header(&Content-Encoding&))) {
response = response.newBuilder()
.removeHeader(&Content-Encoding&)
.removeHeader(&Content-Length&)
responseBody = new GzipSource(transferSource);
responseBody = transferS
到这里又执行完了,responseBody已经被赋值了,他是一个Source接口的实现类。也就是说到这里,这次网络请求就完成了,也收到了服务器返回的数据。
也就是说到这里我们已经分析了:
HttpURLConnection connection = (HttpURLConnection)new URL(url).openConnection();
connection.connect();
接下来就是分析connection.getResponseCode()以及connection.getOutputStream()这两个方法了。&
先看一下getResponseCode()方法:    
@Override public final int getResponseCode() throws IOException {
return getResponse().getResponse().code();
那我们接着看一下getResponse()方法,其实就是直接读取响应头的响应值:      
* Aggressively tries to get the final HTTP response, potentially making
* many HTTP requests in the process in order to cope with redirects and
* authentication.
private HttpEngine getResponse() throws IOException {
initHttpEngine();
if (httpEngine.hasResponse()) {
return httpE
while (true) {
if (!execute(true)) {
Retry retry = processResponseHeaders();
if (retry == Retry.NONE) {
httpEngine.releaseConnection();
return httpE
String retryMethod =
Sink requestBody = httpEngine.getRequestBody();
int responseCode = httpEngine.getResponse().code();
if (responseCode == HTTP_MULT_CHOICE
|| responseCode == HTTP_MOVED_PERM
|| responseCode == HTTP_MOVED_TEMP
|| responseCode == HTTP_SEE_OTHER) {
retryMethod = &GET&;
requestHeaders.removeAll(&Content-Length&);
requestBody = null;
if (requestBody != null && !(requestBody instanceof RetryableSink)) {
throw new HttpRetryException(&Cannot retry streamed HTTP body&, responseCode);
if (retry == Retry.DIFFERENT_CONNECTION) {
httpEngine.releaseConnection();
Connection connection = httpEngine.close();
httpEngine = newHttpEngine(retryMethod, connection, (RetryableSink) requestBody);
接着看一下getOutputStream()的源码:
@Override public final OutputStream getOutputStream() throws IOException {
connect();
BufferedSink sink = httpEngine.getBufferedRequestBody();
if (sink == null) {
throw new ProtocolException(&method does not support a request body: & + method);
} else if (httpEngine.hasResponse()) {
throw new ProtocolException(&cannot write request body after response has been read&);
return sink.outputStream();
顺便再看一下getInputStream()方法:  
@Override public final InputStream getInputStream() throws IOException {
if (!doInput) {
throw new ProtocolException(&This protocol does not support input&);
HttpEngine response = getResponse();
if (getResponseCode() &= HTTP_BAD_REQUEST) {
throw new FileNotFoundException(url.toString());
InputStream result = response.getResponseBodyBytes();
if (result == null) {
throw new ProtocolException(&No
responseCode=& + getResponseCode());
再顺便看一下HttpURLConnection.disconnect()方法,因为这个方法可能很多人不清楚该不该调用他,不过注释说的很明白了:
* Releases this connection so that its resources may be either reused or
* &p&Unlike other Java implementations, this will not necessarily close
* socket connections that can be reused. You can disable all connection
* reuse by setting the
http.keepAlive} system property to
* false} before issuing any HTTP requests.
@Override public final void disconnect() {
if (httpEngine != null) {
httpEngine.close();
看一下HttpEngine.close()方法:
* Release any resources held by this engine. If a connection is still held by
* this engine, it is returned.
public final Connection close() {
if (bufferedRequestBody != null) {
closeQuietly(bufferedRequestBody);
} else if (requestBodyOut != null) {
closeQuietly(requestBodyOut);
if (responseBody == null) {
closeQuietly(connection);
connection = null;
return null;
closeQuietly(responseBody);
closeQuietly(responseBodyBytes);
if (transport != null && !transport.canReuseConnection()) {
closeQuietly(connection);
connection = null;
return null;
Connection result =
connection = null;
继续看一下closeQuietly(connection)方法:
closeable}, ignoring any checked exceptions. Does nothing
closeable} is null.
public static void closeQuietly(Closeable closeable) {
if (closeable != null) {
closeable.close();
} catch (RuntimeException rethrown) {
} catch (Exception ignored) {
再接着看一下Connection.close()方法:
@Override public void close() throws IOException {
socket.close();
再看Socket.close()方法:
package com.squareup.
* Closes the socket. It is not possible to reconnect or rebind to this
* socket thereafter which means a new socket instance has to be created.
* IOException
* if an error occurs while closing the socket.
public synchronized void close() throws IOException {
isClosed = true;
isConnected = false;
localAddress = Inet4Address.ANY;
impl.close();
到这里就都看完了,最后我们再看一下上面用到的连接池,也就是ConnectionPool类: 因为在上面的分析中,我们发现此类贯穿了很多类。&
它为OkHttpClient中的对象,后贯穿到HttpEngine、Connection、HttpConnection等。所以要分析下。
* Manages reuse of HTTP and SPDY connections for reduced network latency. HTTP
* requests that share the same
com.squareup.okhttp.Address} may share a
com.squareup.okhttp.Connection}. This class implements the policy of
* which connections to keep open for future use.
#getDefault() system-wide default} uses system properties for
* tuning parameters:
* &li& http.keepAlive} true if HTTP and SPDY connections should be
* pooled at all. Default is true.
* &li& http.maxConnections} maximum number of idle connections to
* each to keep in the pool. Default is 5.
* &li& http.keepAliveDuration} Time in milliseconds to keep the
* connection alive in the pool before closing it. Default is 5 minutes.
* This property isn't used by
HttpURLConnection}.
* &p&The default instance &i&doesn't&/i& adjust its configuration as system
* properties are changed. This assumes that the applications that set these
* parameters do so before making HTTP connections, and that this class is
* initialized lazily.
public class ConnectionPool {
private static final int MAX_CONNECTIONS_TO_CLEANUP = 2;
private static final long DEFAULT_KEEP_ALIVE_DURATION_MS = 5 * 60 * 1000;
private static final ConnectionPool systemD
String keepAlive = System.getProperty(&http.keepAlive&);
String keepAliveDuration = System.getProperty(&http.keepAliveDuration&);
String maxIdleConnections = System.getProperty(&http.maxConnections&);
long keepAliveDurationMs = keepAliveDuration != null ? Long.parseLong(keepAliveDuration)
: DEFAULT_KEEP_ALIVE_DURATION_MS;
if (keepAlive != null && !Boolean.parseBoolean(keepAlive)) {
systemDefault = new ConnectionPool(0, keepAliveDurationMs);
} else if (maxIdleConnections != null) {
systemDefault = new ConnectionPool(Integer.parseInt(maxIdleConnections), keepAliveDurationMs);
systemDefault = new ConnectionPool(5, keepAliveDurationMs);
/** The maximum number of idle connections for each address. */
private final int maxIdleC
private final long keepAliveDurationNs;
private final LinkedList&Connection& connections = new LinkedList&Connection&();
/** We use a single background thread to cleanup expired connections. */
private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue&Runnable&(),
Util.threadFactory(&OkHttp ConnectionPool&, true));
private final Runnable connectionsCleanupRunnable = new Runnable() {
@Override public void run() {
List&Connection& expiredConnections = new ArrayList&Connection&(MAX_CONNECTIONS_TO_CLEANUP);
int idleConnectionCount = 0;
synchronized (ConnectionPool.this) {
for (ListIterator&Connection& i = connections.listIterator(connections.size());
i.hasPrevious(); ) {
Connection connection = i.previous();
if (!connection.isAlive() || connection.isExpired(keepAliveDurationNs)) {
i.remove();
expiredConnections.add(connection);
if (expiredConnections.size() == MAX_CONNECTIONS_TO_CLEANUP) break;
} else if (connection.isIdle()) {
idleConnectionCount++;
for (ListIterator&Connection& i = connections.listIterator(connections.size());
i.hasPrevious() && idleConnectionCount & maxIdleC ) {
Connection connection = i.previous();
if (connection.isIdle()) {
expiredConnections.add(connection);
i.remove();
--idleConnectionC
for (Connection expiredConnection : expiredConnections) {
Util.closeQuietly(expiredConnection);
public ConnectionPool(int maxIdleConnections, long keepAliveDurationMs) {
this.maxIdleConnections = maxIdleC
this.keepAliveDurationNs = keepAliveDurationMs * 1000 * 1000;
* Returns a snapshot of the connections in this pool, ordered from newest to
* oldest. Waits for the cleanup callable to run if it is currently scheduled.
List&Connection& getConnections() {
waitForCleanupCallableToRun();
synchronized (this) {
return new ArrayList&Connection&(connections);
* Blocks until the executor service has processed all currently enqueued
private void waitForCleanupCallableToRun() {
executorService.submit(new Runnable() {
@Override public void run() {
} catch (Exception e) {
throw new AssertionError();
public static ConnectionPool getDefault() {
return systemD
/** Returns total number of connections in the pool. */
public synchronized int getConnectionCount() {
return connections.size();
/** Returns total number of spdy connections in the pool. */
public synchronized int getSpdyConnectionCount() {
int total = 0;
for (Connection connection : connections) {
if (connection.isSpdy()) total++;
/** Returns total number of http connections in the pool. */
public synchronized int getHttpConnectionCount() {
int total = 0;
for (Connection connection : connections) {
if (!connection.isSpdy()) total++;
/** Returns a recycled connection to
address}, or null if no such connection exists. */
public synchronized Connection get(Address address) {
Connection foundConnection = null;
for (ListIterator&Connection& i = connections.listIterator(connections.size());
i.hasPrevious(); ) {
Connection connection = i.previous();
if (!connection.getRoute().getAddress().equals(address)
|| !connection.isAlive()
|| System.nanoTime() - connection.getIdleStartTimeNs() &= keepAliveDurationNs) {
i.remove();
if (!connection.isSpdy()) {
Platform.get().tagSocket(connection.getSocket());
} catch (SocketException e) {
Util.closeQuietly(connection);
Platform.get().logW(&Unable to tagSocket(): & + e);
foundConnection =
if (foundConnection != null && foundConnection.isSpdy()) {
connections.addFirst(foundConnection);
executorService.execute(connectionsCleanupRunnable);
return foundC
connection} to the pool. The pool may store the connection,
* or close it, as its policy describes.
* &p&It is an error to use
connection} after calling this method.
public void recycle(Connection connection) {
if (connection.isSpdy()) {
if (!connection.isAlive()) {
Util.closeQuietly(connection);
Platform.get().untagSocket(connection.getSocket());
} catch (SocketException e) {
Platform.get().logW(&Unable to untagSocket(): & + e);
Util.closeQuietly(connection);
synchronized (this) {
connections.addFirst(connection);
connection.incrementRecycleCount();
connection.resetIdleStartTime();
executorService.execute(connectionsCleanupRunnable);
* Shares the SPDY connection with the pool. Callers to this method may
* continue to use
connection}.
public void share(Connection connection) {
if (!connection.isSpdy()) throw new IllegalArgumentException();
executorService.execute(connectionsCleanupRunnable);
if (connection.isAlive()) {
synchronized (this) {
connections.addFirst(connection);
/** Close and remove all connections in the pool. */
public void evictAll() {
List&Connection&
synchronized (this) {
connections = new ArrayList&Connection&(this.connections);
this.connections.clear();
for (int i = 0, size = connections.size(); i & i++) {
Util.closeQuietly(connections.get(i));
Good Luck!
* Manages reuse of HTTP and SPDY connections for reduced network latency. HTTP
* requests that share the same
com.squareup.okhttp.Address} may share a
com.squareup.okhttp.Connection}. This class implements the policy of
* which connections to keep open for future use.
#getDefault() system-wide default} uses system properties for
* tuning parameters:
* &li& http.keepAlive} true if HTTP and SPDY connections should be
* pooled at all. Default is true.
* &li& http.maxConnections} maximum number of idle connections to
* each to keep in the pool. Default is 5.
* &li& http.keepAliveDuration} Time in milliseconds to keep the
* connection alive in the pool before closing it. Default is 5 minutes.
* This property isn't used by
HttpURLConnection}.
* &p&The default instance &i&doesn't&/i& adjust its configuration as system
* properties are changed. This assumes that the applications that set these
* parameters do so before making HTTP connections, and that this class is
* initialized lazily.
public class ConnectionPool {
private static final int MAX_CONNECTIONS_TO_CLEANUP = 2;
private static final long DEFAULT_KEEP_ALIVE_DURATION_MS = 5 * 60 * 1000;
private static final ConnectionPool systemD
String keepAlive = System.getProperty(&http.keepAlive&);
String keepAliveDuration = System.getProperty(&http.keepAliveDuration&);
String maxIdleConnections = System.getProperty(&http.maxConnections&);
long keepAliveDurationMs = keepAliveDuration != null ? Long.parseLong(keepAliveDuration)
: DEFAULT_KEEP_ALIVE_DURATION_MS;
if (keepAlive != null && !Boolean.parseBoolean(keepAlive)) {
systemDefault = new ConnectionPool(0, keepAliveDurationMs);
} else if (maxIdleConnections != null) {
systemDefault = new ConnectionPool(Integer.parseInt(maxIdleConnections), keepAliveDurationMs);
systemDefault = new ConnectionPool(5, keepAliveDurationMs);
/** The maximum number of idle connections for each address. */
private final int maxIdleC
private final long keepAliveDurationNs;
private final LinkedList&Connection& connections = new LinkedList&Connection&();
/** We use a single background thread to cleanup expired connections. */
private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue&Runnable&(),
Util.threadFactory(&OkHttp ConnectionPool&, true));
private final Runnable connectionsCleanupRunnable = new Runnable() {
@Override public void run() {
List&Connection& expiredConnections = new ArrayList&Connection&(MAX_CONNECTIONS_TO_CLEANUP);
int idleConnectionCount = 0;
synchronized (ConnectionPool.this) {
for (ListIterator&Connection& i = connections.listIterator(connections.size());
i.hasPrevious(); ) {
Connection connection = i.previous();
if (!connection.isAlive() || connection.isExpired(keepAliveDurationNs)) {
i.remove();
expiredConnections.add(connection);
if (expiredConnections.size() == MAX_CONNECTIONS_TO_CLEANUP) break;
} else if (connection.isIdle()) {
idleConnectionCount++;
for (ListIterator&Connection& i = connections.listIterator(connections.size());
i.hasPrevious() && idleConnectionCount & maxIdleC ) {
Connection connection = i.previous();
if (connection.isIdle()) {
expiredConnections.add(connection);
i.remove();
--idleConnectionC
for (Connection expiredConnection : expiredConnections) {
Util.closeQuietly(expiredConnection);
public ConnectionPool(int maxIdleConnections, long keepAliveDurationMs) {
this.maxIdleConnections = maxIdleC
this.keepAliveDurationNs = keepAliveDurationMs * 1000 * 1000;
* Returns a snapshot of the connections in this pool, ordered from newest to
* oldest. Waits for the cleanup callable to run if it is currently scheduled.
List&Connection& getConnections() {
waitForCleanupCallableToRun();
synchronized (this) {
return new ArrayList&Connection&(connections);
* Blocks until the executor service has processed all currently enqueued
private void waitForCleanupCallableToRun() {
executorService.submit(new Runnable() {
@Override public void run() {
} catch (Exception e) {
throw new AssertionError();
public static ConnectionPool getDefault() {
return systemD
/** Returns total number of connections in the pool. */
public synchronized int getConnectionCount() {
return connections.size();
/** Returns total number of spdy connections in the pool. */
public synchronized int getSpdyConnectionCount() {
int total = 0;
for (Connection connection : connections) {
if (connection.isSpdy()) total++;
/** Returns total number of http connections in the pool. */
public synchronized int getHttpConnectionCount() {
int total = 0;
for (Connection connection : connections) {
if (!connection.isSpdy()) total++;
/** Returns a recycled connection to
address}, or null if no such connection exists. */
public synchronized Connection get(Address address) {
Connection foundConnection = null;
for (ListIterator&Connection& i = connections.listIterator(connections.size());
i.hasPrevious(); ) {
Connection connection = i.previous();
if (!connection.getRoute().getAddress().equals(address)
|| !connection.isAlive()
|| System.nanoTime() - connection.getIdleStartTimeNs() &= keepAliveDurationNs) {
i.remove();
if (!connection.isSpdy()) {
Platform.get().tagSocket(connection.getSocket());
} catch (SocketException e) {
Util.closeQuietly(connection);
Platform.get().logW(&Unable to tagSocket(): & + e);
foundConnection =
if (foundConnection != null && foundConnection.isSpdy()) {
connections.addFirst(foundConnection);
executorService.execute(connectionsCleanupRunnable);
return foundC
connection} to the pool. The pool may store the connection,
* or close it, as its policy describes.
* &p&It is an error to use
connection} after calling this method.
public void recycle(Connection connection) {
if (connection.isSpdy()) {
if (!connection.isAlive()) {
Util.closeQuietly(connection);
Platform.get().untagSocket(connection.getSocket());
} catch (SocketException e) {
Platform.get().logW(&Unable to untagSocket(): & + e);
Util.closeQuietly(connection);
synchronized (this) {
connections.addFirst(connection);
connection.incrementRecycleCount();
connection.resetIdleStartTime();
executorService.execute(connectionsCleanupRunnable);
* Shares the SPDY connection with the pool. Callers to this method may
* continue to use
connection}.
public void share(Connection connection) {
if (!connection.isSpdy()) throw new IllegalArgumentException();
executorService.execute(connectionsCleanupRunnable);
if (connection.isAlive()) {
synchronized (this) {
connections.addFirst(connection);
/** Close and remove all connections in the pool. */
public void evictAll() {
List&Connection&
synchronized (this) {
connections = new ArrayList&Connection&(this.connections);
this.connections.clear();
for (int i = 0, size = connections.size(); i & i++) {
Util.closeQuietly(connections.get(i));
文章比较长。。看了一天,就一口气写完了,希望能看到这里。
【上篇】【下篇】

我要回帖

更多关于 c httpurlconnection 的文章

 

随机推荐