DownloadProvider源码解析——与Volley对比

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的工具去读的话可能不符合自己想要的效果,还是要自己去继承扩展或者直接读取输入流转换成自己想要的类型。

时间: 2024-10-01 04:17:17

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

volley源码解析(一)--volley的使用和架构

Volley是一款由Google 推出的 Android 异步网络请求框架和图片加载框架,特别适合数据量小,通信频繁的网络操作. 大家可以在这个地址https://android.googlesource.com/platform/frameworks/volley/下载源码 Volley 的主要特点 (1). 扩展性强.Volley 中大多是基于接口的设计,可配置性强. (2). 一定程度符合 Http 规范,包括返回 ResponseCode(2xx.3xx.4xx.5xx)的处理,请求头的

volley源码解析(三)--Volley核心之RequestQueue类

上一篇文章给大家说明了Request<T>的内部结构,对于这个类而言,volley让我们关注的主要请求获得响应以后,怎么根据自己的需要解析响应,然后在主线程中回调监听器的方法,至于是怎么获得响应的,线程又是怎么开启的,都跟Request无关. 前面已经提到,Request会放在队列里面等待线程的提取,RequestQueue类作为volley的核心类,可以说是连接请求与响应的桥梁.另外,RequestQueue类作为一个集合,还给我们统一管理请求带来更大的方便,这样的思想是很值得我们学习的.

Volley 源码解析(转)

项目:Volley,分析者:grumoon,校对者:Trinea 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon,校对者:huxian99.Trinea,校对状态:完成 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. 名字由来:a burs

Volley 源码解析(转自codeKK)

Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon,校对者:huxian99.Trinea,校对状态:完成 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. 名字由来:a burst or emission of many

Volley 源码解析

Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon,校对者:huxian99.Trinea,校对状态:完成 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. 名字由来:a burst or emission of many

Volley 源码解析&lt;转&gt;

Volley 源码解析 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. 名字由来:a burst or emission of many things or a large amount at once发布演讲时候的配图 从名字由来和配图中无数急促的火箭可以看出 Volley 的特点:特别适合数据量小,通信频繁的网络操作.(个人认为 Android 应用中绝大多数的网

volley源码解析(七)--最终目的之Response&lt;T&gt;

在上篇文章中,我们最终通过网络,获取到了HttpResponse对象 HttpResponse是android包里面的一个类,然后为了更高的扩展性,我们在BasicNetwork类里面看到,Volley将其包装成一个Volley自己的对象NetworkResponse 另外,在BasicNetwork类中我们也注意到,对HttpResponse包装成NetworkResponse的过程中,使用HttpResponse的Inputstream,将数据保存在一个byte[]数组中. BasicNet

Android开发——Volley源码解析

0. 前言   其实写这篇文章只为一个目的,虽然Volley用起来很爽,但是面试官问你人家内部是怎么实现呢,没看过源码的话,在面试官眼里你和一个拿着一本Volley使用手册的高中生没啥区别.还是那句话说得好,会用一回事,深入理解又是另一回事了. 1.  Volley源码解析 1.1  Volley入口 Volley首先获取到的是RequestQueue实例.源码中则直接调用了newRequestQueue方法. public static RequestQueue newRequestQueue

Volley框架使用及源码解析

1. Volley特点 (1) 特别适合数据量小,通信频繁的网络操作. (2) 扩展性强.Volley 中大多是基于接口的设计,可根据需要自行定制. (3) 一定程度符合 Http 规范,包括返回 ResponseCode(2xx.3xx.4xx.5xx)的处理,请求头的处理, 缓存机制的支持等.并支持重试及优先级定义. (4) 提供简便的图片加载工具 GitHub地址:https//github.com/mcxiaoke/android-volley 2. 概念介绍 Request:表示一个请