1.AndroidHttpClient的创建

DownloadManager:

在DownloadThread的run方法里

public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//从DownloadInfo转化成State
State state = new State(mInfo);
AndroidHttpClient client = null;
PowerManager.WakeLock wakeLock = null;
int finalStatus = Downloads.STATUS_UNKNOWN_ERROR; try {
//阻止后台休眠
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
wakeLock.acquire(); if (Constants.LOGV) {
Log.v(Constants.TAG, "initiating download for " + mInfo.mUri);
} client = AndroidHttpClient.newInstance(userAgent(), mContext);

userAgent方法,如果DownloadInfo中有设置ua就用设置了的UA,没有就用默认的"AndroidDownloadManager“

/**
* Returns the user agent provided by the initiating app, or use the default one
*/
private String userAgent() {
String userAgent = mInfo.mUserAgent;
if (userAgent != null) {
}
if (userAgent == null) {
userAgent = Constants.DEFAULT_USER_AGENT;
}
return userAgent;
}

Volley:

Volley中的HttpClient创建是在请求线程发出前,如果获取包名异常则使用"volley/0"作为ua,如果SDK版本大于9就用HttpUrlStack

String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
} if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
 }

Volley是根据SDK版本区分,2.3之后用Hurl之前就用AndroidHttpClient,Hurl在后面的版本支持gzip和请求缓存。

2.performRequest执行请求

由于DownloadManager是用HttpClient和HttpGet来执行请求的,所以获取到一个HttpResponse,然后得到一个输入流,然后把流读出来写入到一个文件。

DM:

DownloadThread中run方法,executeDownload执行下载

boolean finished = false;
while (!finished) {
Log.i(Constants.TAG, "Initiating request for download " + mInfo.mId);
HttpGet request = new HttpGet(state.mRequestUri);
try {
//执行下载
executeDownload(state, client, request);
finished = true;
} catch (RetryDownload exc) {
// fall through
} finally {
request.abort();
request = null;
}
}

请求过程,设置目标文件,添加请求头(断点续传),检查网络,发送请求,处理异常Http状态码,获取输入流转移数据到目标文件。

setupDestinationFile(state, innerState);
addRequestHeaders(innerState, request); // check just before sending the request to avoid using an invalid
// connection at all
checkConnectivity(state);//发起请求检查连接 //发送请求获取Response
HttpResponse response = sendRequest(state, client, request);
handleExceptionalStatus(state, innerState, response); if (Constants.LOGV) {
Log.v(Constants.TAG, "received response for " + mInfo.mUri);
} processResponseHeaders(state, innerState, response);
//从获取输入流
InputStream entityStream = openResponseEntity(state, response);
//数据读取
transferData(state, innerState, data, entityStream);

Volley:

NetworkDispatch中run方法,请求队列取出一个请求,检查是否需要取消,设置标签,发送请求,获取网络响应,转化为Responces,分发结果

public void run() {
    //这里和dm中一样,线程设置为Backgound优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
try {
// Take a request from the queue.队列中取出一个请求
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
} try {
request.addMarker("network-queue-take"); // If the request was cancelled already, do not perform the
// network request.检查是否需要取消
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
} addTrafficStatsTag(request); // Perform the network request.执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete"); // If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
} // Parse the response here on the worker thread.解析网络响应
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete"); // Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
} // Post the response back.
request.markDelivered();
mDelivery.postResponse(request, response);//分发响应结果
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
}

真正的网络请求过程在这个BasicNetwork的performRequest方法,最终返回一个含响应状态码和转成byte数组的结果对象

// Gather headers.获取HTTP头部
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode(); responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.处理缓存校验
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry() == null ? null : request.getCacheEntry().data,
responseHeaders, true);
} // Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {//把HttpEntity转换成byte数组
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
} // if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine); if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
          //返回一个带状态码和byte数组和响应头部的NetworkResponse对象
          returnnew NetworkResponse(statusCode, responseContents, responseHeaders, false);

错了,上面还只是个代理,真正执行请求的还是HttpClient和HttpUrlConnection

先来看HttpClient的:

public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest);
}

这个过程其实还是很简单,首先转换头部,然后把Volley中Request转换成Apache的HttpUriRequest,然后调用HttpClient.ececute(HttpUriRequest)方法得到一个HttpResponse

再来看他是怎么把Volley的Request转换成Apache的HttpUriRequest

static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Method.GET://GET不需要设置参数
return new HttpGet(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
} }

然后设置Entity,Volley内request.getBody()方法返回一个byte数组,把这个数组包装成一个HttpEntity然后就用HttpRequest.setEntity方法设置进去

private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
Request<?> request) throws AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
HttpEntity entity = new ByteArrayEntity(body);
httpRequest.setEntity(entity);
}
}

上面都是HttpClient的实现方法,再来看HttpUrlConnection的实现

HurlStack的performRequest方法

String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
    //构建Url
URL parsedUrl = new URL(url);
    //打开连接
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
    //设置请求方法,Get或Post
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
    //状态码
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
    //实体内容
response.setEntity(entityFromConnection(connection));
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;

打开连接的实现方法:

private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
HttpURLConnection connection = createConnection(url); int timeoutMs = request.getTimeoutMs();
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false);
connection.setDoInput(true); // use caller-provided custom SslSocketFactory, if any, for HTTPS
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
} return connection;
}
protected HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}

Hurl中把获取出来的输入流转换成HttpEntity,setContent(InputStream),可以看出来Hurl还是转换成Apache的标准

3.IO流数据转移

要实现进度监听也只能在这一步去实现

dm的数据转移在DownloadThread的transferData方法,innerState.mBytesSoFar就是当前进度,onProgress(long progress)把当前进度通知出去

private void transferData(State state, InnerState innerState, byte[] data,
InputStream entityStream) throws StopRequest {
for (;;) {
//读取Response
int bytesRead = readFromResponse(state, innerState, data, entityStream);
if (bytesRead == -1) { // success, end of stream already reached
//读完了去处理
handleEndOfStream(state, innerState);
return;
} state.mGotData = true;
//把byte数组写到目标文件
writeDataToDestination(state, data, bytesRead);
innerState.mBytesSoFar += bytesRead;
reportProgress(state, innerState);//报告下载进度 if (Constants.LOGVV) {
Log.v(Constants.TAG, "downloaded " + innerState.mBytesSoFar + " for " + mInfo.mUri);
} checkPausedOrCanceled(state);
}
}

Volley则是把输入流读到一个byte数组池,只要存在读流的地方就可以射出进度

/** Reads the contents of HttpEntity into a byte[]. */
private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
PoolingByteArrayOutputStream bytes =
new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
byte[] buffer = null;
try {
InputStream in = entity.getContent();
if (in == null) {
throw new ServerError();
}
buffer = mPool.getBuf(1024);
int count;
while ((count = in.read(buffer)) != -1) {
bytes.write(buffer, 0, count);
}
return bytes.toByteArray();
} finally {
try {
// Close the InputStream and release the resources by "consuming the content".
entity.consumeContent();
} catch (IOException e) {
// This can happen if there was an exception above that left the entity in
// an invalid state.
VolleyLog.v("Error occured when calling consumingContent");
}
mPool.returnBuf(buffer);
bytes.close();
}
}

dm从输入流读出来写进FileOutputStream,volley是读到ByteArrayOutputStream,HttpUrlConnection只能得到一个输入流,HttpClient能获取一个HttpEntity,然后可以通过Apache的工具直接转换成要的类型,当然HttpEntity也可以直接获得一个输入流,输入流也可以转成一个HttpEntity,如果用Apache的工具去读的话可能不符合自己想要的效果,还是要自己去继承扩展或者直接读取输入流转换成自己想要的类型。

DownloadProvider源码解析——与Volley对比的更多相关文章

  1. 源码解析-Volley(转自codeKK)

    Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon ...

  2. Volley 源码解析(转)

    项目:Volley,分析者:grumoon,校对者:Trinea 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:V ...

  3. Volley 源码解析

    Volley 源码解析 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. ...

  4. 【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析

    通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文[安卓网络请求开源框架Volley源码解析系列]初识Volley及其基本用法.如StringRequest用来请求一 ...

  5. TongWEB与JOnAS 对比,国产中间件战斗机东方通TongWEB源码解析

    转自网址: http://bbs.51cto.com/thread-489819-1-1.html 首先需要声明的是,本人出于技术爱好的角度,以下的文字只是对所看到的一些情况的罗列,偶尔附加个人的一些 ...

  6. Volley源码解析(三) 有缓存机制的情况走缓存请求的源码分析

    Volley源码解析(三) 有缓存机制的情况走缓存请求的源码分析 Volley之所以高效好用,一个在于请求重试策略,一个就在于请求结果缓存. 通过上一篇文章http://www.cnblogs.com ...

  7. # Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析#

    Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析 Volley源码一共40多个类和接口.除去一些工具类的实现,核心代码只有20多个类.所以相对来说分析起来没有那么吃力.但是要想分析透 ...

  8. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  9. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

随机推荐

  1. [Java web]Spring+Struts2+Hibernate整合过程

    摘要 最近一直在折腾java web相关内容,这里就把最近学习的spring+struts2+hibernate进行一个整合,也就是大家经常说的ssh. 环境 工具IDE :Idea 2018 数据库 ...

  2. dhtmlxtree 节点 展开收缩:新增了直接点 文本内容 也 实现了 展开收缩 功能(并记住了展开、收缩状态)

    dhtmlxtree 节点 展开收缩通常情况我们按 +- 就实现了 展开收缩 功能,为了方便我们新增了直接点 文本内容 也 实现了 展开收缩 功能(并记住了展开.收缩状态) tree = new dh ...

  3. 如何在IntelliJ IDEA中使用Git .ignore插件忽略不必要提交的文件

    参考  https://blog.csdn.net/qq_34590097/article/details/56284935 最近初学Git,而且在使用的IDE是IntelliJ IDEA,发现IDE ...

  4. 隐藏控件--HiddenField控件

    HiddenField控件百度查的结果(帮助大家对比理解): HiddenField控件顾名思义就是隐藏输入框的服务器控件,它能让你保存那些不需要显示在页面上的且对安全性要求不高的数据.也许这个时候应 ...

  5. Java高编译低运行错误(ConcurrentHashMap.keySet)

    Java高编译低运行错误(ConcurrentHashMap.keySet) 调了一天: https://www.jianshu.com/p/f4996b1ccf2f

  6. 【Zookeeper】源码分析之服务器(二)之ZooKeeperServer

    一.前言 前面阐述了服务器的总体框架,下面来分析服务器的所有父类ZooKeeperServer. 二.ZooKeeperServer源码分析 2.1 类的继承关系 public class ZooKe ...

  7. vbox磁盘空间如何扩容

    vbox磁盘空间如何扩容   为虚拟机硬盘扩容(Oracle VM VirtualBox) VBoxManage modifyhd         <uuid>|<filename& ...

  8. swift3 单例写法

    import UIKit class SingleOnce { // 单例 static let shared = SingleOnce.init() private init(){} // 其他方法 ...

  9. String为什么是不可变的?

    面试官Q1:请问为什么String是不可变的,能谈谈吗? 我们知道不管是面试初级.中级还是高级Java开发工程师,String永远都是一个绕不开的话题,而且问的问题也是各不相同,下面我们从几个角度来看 ...

  10. linux nfs共享文件

    linux文件共享可以有多种方式:samba,nfs,ftp等等 nfs在linux之间效率高些: function nfs(){ share_folder="/data1 192.168. ...