android httpengine,Android每周一轮子:HttpUrlConnection

序言

接着上一篇的Volley,本篇原定计划是OkHttp的,但是在分析道OKhttp底层时,对于IO的包装等等特性,需要一个可参照的对比的例子,比如HttpURLConnection等,通过这种对比,才可以看的出其优势。对于Volley,其实只是对于底层网络库的封装,真正的网络请求的发起还是通过HttpStack来执行,HttpStack在此之前可选的为HttpClient和HttpURLConnection。这里针对HttpURLConnection展开进行分析。分析这样的一个网络库是如何实现的。本代码基于Android 4.3,4.4和其之后底层的实现采用了OkHttp。

基础使用

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。

1460000013678707

源码实现

首先是获取HttpURLConnection

1460000013678708

首先调用了URL的openConnection方法。

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());

}

}

1460000013678709

对于StreamHandler,有多个子类,分别可以用来进行http,https,ftp等协议流的处理。下面是HttpHandler的创建过程。

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);

}

}

1460000013678710

首先判断是否已经有创建,从Handler列表中获取,如果没有判断Handler工厂是否存在,如果不存在,加载本地的指定路径,从中加载并创建相应的实例,最后如果本地路径也没有,则根据协议的类型,创建相应的协议Handler。

HttpHandler

这里我们只针对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;

}

HttpEngine的建立

调用getResponse获得HttpEngine对象,从中获取请求的内容。

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,并创建其实例。

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 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 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);

}

1460000013678711

小结

对于HttpURLConnection库,是一个相对比较简单的网络库,最开始通过根据设置的URL信息,创建一个Socket连接,然后获得Socket连接后得到Socket的InputStream和OutputStream,然后通过其获取数据和写入数据,其内部提供的功能比较少,仅限于帮助我们做一些简单的http的包装,核心类是HttpConnection,HttpEngine两个类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值