接着上一篇的Volley,本篇原定计划是OkHttp的,可是在分析道OKhttp底层时,对于IO的包装等等特性,须要一个可参照的对比的例子,好比HttpURLConnection等,经过这种对比,才能够看的出其优点。对于Volley,其实只是对于底层网络库的封装,真正的网络请求的发起仍是经过HttpStack来执行,HttpStack在此以前可选的为HttpClient和HttpURLConnection。这里针对HttpURLConnection展开进行分析。分析这样的一个网络库是如何实现的。本代码基于Android 4.3,4.4和其以后底层的实现采用了OkHttp。java
URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
} finally {
urlConnection.disconnect();
}
复制代码
创建链接以后,得到一个InputStream,咱们就能够从中读取链接创建后的返回数据。若是咱们须要在请求中添加参数也能够经过获取一个输出流,在输出流中写入咱们的请求数据。而其底层背后就是创建的一个Socket。android
首先是获取HttpURLConnection
缓存
首先调用了URL的openConnection方法。bash
public URLConnection openConnection() throws java.io.IOException {
return handler.openConnection(this);
}
复制代码
经过源码能够看出,对于链接的操做都是经过URLStreamHandler
来进行的,对于URLStreamHandler的建立是在URL的构造函数之中。网络
public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException {
....
if (streamHandler == null) {
setupStreamHandler();
if (streamHandler == null) {
throw new MalformedURLException("Unknown protocol: " + protocol);
}
}
try {
streamHandler.parseURL(this, spec, schemeSpecificPartStart, spec.length());
} catch (Exception e) {
throw new MalformedURLException(e.toString());
}
}
复制代码
对于StreamHandler,有多个子类,分别能够用来进行http,https,ftp等协议流的处理。下面是HttpHandler的建立过程。socket
void setupStreamHandler() {
//检查是否有缓存的处理相应协议的StreamHandler
streamHandler = streamHandlers.get(protocol);
if (streamHandler != null) {
return;
}
//若是streamHandlerFactory不为空,经过其建立streamHandler,并将其缓存下来
if (streamHandlerFactory != null) {
streamHandler = streamHandlerFactory.createURLStreamHandler(protocol);
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
return;
}
}
//从用户提供的包中加载相应的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";
try {
Class<?> c = contextClassLoader.loadClass(className);
streamHandler = (URLStreamHandler) c.newInstance();
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
return;
} catch (IllegalAccessException ignored) {
} catch (InstantiationException ignored) {
} catch (ClassNotFoundException ignored) {
}
}
}
// 若是用户没有提供,则会根据协议的要求,加载相应的Handler
if (protocol.equals("file")) {
streamHandler = new FileHandler();
} else if (protocol.equals("ftp")) {
streamHandler = new FtpHandler();
} else if (protocol.equals("http")) {
streamHandler = new HttpHandler();
} else if (protocol.equals("https")) {
streamHandler = new HttpsHandler();
} else if (protocol.equals("jar")) {
streamHandler = new JarHandler();
}
//将Handler加入到缓存
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
}
复制代码
首先判断是否已经有建立,从Handler列表中获取,若是没有判断Handler工厂是否存在,若是不存在,加载本地的指定路径,从中加载并建立相应的实例,最后若是本地路径也没有,则根据协议的类型,建立相应的协议Handler。ide
这里咱们只针对Http协议来看,跟进下HttpHandler的源码,来了解一下其实现。Handler的链接创建,经过HttpURLConnectionImpl
实例来进行。函数
protected URLConnection openConnection(URL u) throws IOException {
return new HttpURLConnectionImpl(u, getDefaultPort());
}
复制代码
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.getResponseBody();
if (result == null) {
throw new IOException("No response body exists; responseCode=" + getResponseCode());
}
return result;
}
复制代码
调用getResponse得到HttpEngine对象,从中获取请求的内容。ui
private HttpEngine getResponse() throws IOException {
//初始化HttpEngine
initHttpEngine();
//判断HttpEngine若是有响应直接返回
if (httpEngine.hasResponse()) {
return httpEngine;
}
while (true) {
try {
httpEngine.sendRequest();
httpEngine.readResponse();
} catch (IOException e) {
OutputStream requestBody = httpEngine.getRequestBody();
if (httpEngine.hasRecycledConnection()
&& (requestBody == null || requestBody instanceof RetryableOutputStream)) {
httpEngine.release(false);
httpEngine = newHttpEngine(method, rawRequestHeaders, null,
(RetryableOutputStream) requestBody);
continue;
}
httpEngineFailure = e;
throw e;
}
Retry retry = processResponseHeaders();
if (retry == Retry.NONE) {
httpEngine.automaticallyReleaseConnectionToPool();
return httpEngine;
}
String retryMethod = method;
OutputStream requestBody = httpEngine.getRequestBody();
int responseCode = getResponseCode();
if (responseCode == HTTP_MULT_CHOICE || responseCode == HTTP_MOVED_PERM
|| responseCode == HTTP_MOVED_TEMP || responseCode == HTTP_SEE_OTHER) {
retryMethod = HttpEngine.GET;
requestBody = null;
}
if (requestBody != null && !(requestBody instanceof RetryableOutputStream)) {
throw new HttpRetryException("Cannot retry streamed HTTP body",
httpEngine.getResponseCode());
}
if (retry == Retry.DIFFERENT_CONNECTION) {
httpEngine.automaticallyReleaseConnectionToPool();
} else {
httpEngine.markConnectionAsRecycled();
}
httpEngine.release(true);
//建立HttpEngine
httpEngine = newHttpEngine(retryMethod, rawRequestHeaders,
httpEngine.getConnection(), (RetryableOutputStream) requestBody);
}
}
复制代码
根据上述方法中的核心调用,逐步展开,首先是初始化HttpEngine,并建立其实例。this
private void initHttpEngine() throws IOException {
if (httpEngineFailure != null) {
throw httpEngineFailure;
} else if (httpEngine != null) {
return;
}
connected = true;
try {
if (doOutput) {
if (method == HttpEngine.GET) {
// they are requesting a stream to write to. This implies a POST method
method = HttpEngine.POST;
} else if (method != HttpEngine.POST && method != HttpEngine.PUT) {
// If the request method is neither POST nor PUT, then you're not writing throw new ProtocolException(method + " does not support writing"); } } httpEngine = newHttpEngine(method, rawRequestHeaders, null, null); } catch (IOException e) { httpEngineFailure = e; throw e; } } 复制代码
protected HttpEngine newHttpEngine(String method, RawHeaders requestHeaders,
HttpConnection connection, RetryableOutputStream requestBody) throws IOException {
return new HttpEngine(this, method, requestHeaders, connection, requestBody);
}
复制代码
在newHttpEngie中,new了一个HttpEngine的实例。
public HttpEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
HttpConnection connection, RetryableOutputStream requestBodyOut) throws IOException {
this.policy = policy;
this.method = method;
this.connection = connection;
this.requestBodyOut = requestBodyOut;
try {
uri = policy.getURL().toURILenient();
} catch (URISyntaxException e) {
throw new IOException(e);
}
this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders));
}
复制代码
在执行完了HttpEngine的初始化方法以后,调用了其sendRequest方法,首先会进行缓存的判断,最后会判断其是否须要链接,若是须要,则会调用相应的链接方法:sendSocketRequest。
public final void sendRequest() throws IOException {
if (responseSource != null) {
return;
}
prepareRawRequestHeaders();
initResponseSource();
if (responseCache instanceof ExtendedResponseCache) {
((ExtendedResponseCache) responseCache).trackResponse(responseSource);
}
if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) {
if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
IoUtils.closeQuietly(cachedResponseBody);
}
this.responseSource = ResponseSource.CACHE;
this.cacheResponse = GATEWAY_TIMEOUT_RESPONSE;
RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders());
setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody());
}
if (responseSource.requiresConnection()) {
//放松socket创建请求
sendSocketRequest();
} else if (connection != null) {
HttpConnectionPool.INSTANCE.recycle(connection);
connection = null;
}
}
复制代码
首先判断connection是否为空,若是为空,调用connect方法,而后得到该链接的OutputStream,InputStream。
private void sendSocketRequest() throws IOException {
if (connection == null) {
connect();
}
if (socketOut != null || requestOut != null || socketIn != null) {
throw new IllegalStateException();
}
//创建socket后,返回其读写流
socketOut = connection.getOutputStream();
requestOut = socketOut;
socketIn = connection.getInputStream();
if (hasRequestBody()) {
initRequestBodyOut();
}
}
复制代码
实际链接过程调用
protected void connect() throws IOException {
if (connection == null) {
connection = openSocketConnection();
}
}
复制代码
开启Socket链接,这里调用了HttpConnect的链接函数。
protected final HttpConnection openSocketConnection() throws IOException {
HttpConnection result = HttpConnection.connect(uri, getSslSocketFactory(),
policy.getProxy(), requiresTunnel(), policy.getConnectTimeout());
Proxy proxy = result.getAddress().getProxy();
if (proxy != null) {
policy.setProxy(proxy);
}
result.setSoTimeout(policy.getReadTimeout());
return result;
}
复制代码
对于具体的链接任务交给了HttpConnection来处理,调用其链接方法。会从链接池中获取相应的链接,调用其get方法。
public static HttpConnection connect(URI uri, SSLSocketFactory sslSocketFactory,
Proxy proxy, boolean requiresTunnel, int connectTimeout) throws IOException {
if (proxy != null) {
Address address = (proxy.type() == Proxy.Type.DIRECT)
? new Address(uri, sslSocketFactory)
: new Address(uri, sslSocketFactory, proxy, requiresTunnel);
return HttpConnectionPool.INSTANCE.get(address, connectTimeout);
}
/*
* Try connecting to each of the proxies provided by the ProxySelector
* until a connection succeeds.
*/
ProxySelector selector = ProxySelector.getDefault();
List<Proxy> proxyList = selector.select(uri);
if (proxyList != null) {
for (Proxy selectedProxy : proxyList) {
if (selectedProxy.type() == Proxy.Type.DIRECT) {
continue;
}
try {
Address address = new Address(uri, sslSocketFactory,
selectedProxy, requiresTunnel);
return HttpConnectionPool.INSTANCE.get(address, connectTimeout);
} catch (IOException e) {
// failed to connect, tell it to the selector
selector.connectFailed(uri, selectedProxy.address(), e);
}
}
}
/*
* Try a direct connection. If this fails, this method will throw.
*/
return HttpConnectionPool.INSTANCE.get(new Address(uri, sslSocketFactory), connectTimeout);
}
复制代码
根据传递的请求,从HttpConnectionPool中获取HttpConnection链接。当其中不存在该链接的时候,从新建立一个实例,而后返回。
public HttpConnection get(HttpConnection.Address address, int connectTimeout)
throws IOException {
// First try to reuse an existing HTTP connection.
synchronized (connectionPool) {
List<HttpConnection> connections = connectionPool.get(address);
while (connections != null) {
HttpConnection connection = connections.remove(connections.size() - 1);
if (connections.isEmpty()) {
connectionPool.remove(address);
connections = null;
}
if (connection.isEligibleForRecycling()) {
// Since Socket is recycled, re-tag before using
Socket socket = connection.getSocket();
SocketTagger.get().tag(socket);
return connection;
}
}
}
//当咱们没法找到一个可用的链接,这个时候,咱们须要从新建立一个新的链接
return address.connect(connectTimeout);
}
复制代码
Address 为HttpConnection的一个内部类。
public HttpConnection connect(int connectTimeout) throws IOException {
return new HttpConnection(this, connectTimeout);
}
复制代码
此时会再建立一个新的链接 ,在HttpConnection的构造函数之中是真正的socket链接创建的地方。
private HttpConnection(Address config, int connectTimeout) throws IOException {
this.address = config;
Socket socketCandidate = null;
InetAddress[] addresses = InetAddress.getAllByName(config.socketHost);
for (int i = 0; i < addresses.length; i++) {
socketCandidate = (config.proxy != null && config.proxy.type() != Proxy.Type.HTTP)
? new Socket(config.proxy)
: new Socket();
try {
socketCandidate.connect(
new InetSocketAddress(addresses[i], config.socketPort), connectTimeout);
break;
} catch (IOException e) {
if (i == addresses.length - 1) {
throw e;
}
}
}
this.socket = socketCandidate;
}
复制代码
请求体的写入,得到Socket的写入流,而后将咱们的请求数据写入
private void writeRequestHeaders(int contentLength) throws IOException {
if (sentRequestMillis != -1) {
throw new IllegalStateException();
}
RawHeaders headersToSend = getNetworkRequestHeaders();
byte[] bytes = headersToSend.toHeaderString().getBytes(Charsets.ISO_8859_1);
if (contentLength != -1 && bytes.length + contentLength <= MAX_REQUEST_BUFFER_LENGTH) {
requestOut = new BufferedOutputStream(socketOut, bytes.length + contentLength);
}
sentRequestMillis = System.currentTimeMillis();
requestOut.write(bytes);
}
复制代码
对于HttpURLConnection库,是一个相对比较简单的网络库,最开始经过根据设置的URL信息,建立一个Socket链接,而后得到Socket链接后获得Socket的InputStream和OutputStream,而后经过其获取数据和写入数据,其内部提供的功能比较少,仅限于帮助咱们作一些简单的http的包装,核心类是HttpConnection,HttpEngine两个类。