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

  • 相关阅读:
    网络编程基础【day10】:多线程效果演示(二)
    网络编程基础【day10】:进程与线程介绍(一 )
    第四模块:网络编程基础
    第三模块:面向对象(目录)
    网络编程基础【day09】:堡垒机前戏(十一)
    网络编程基础【day09】:socketserver进阶(十)
    网络编程基础【day09】:socketserver使用(九)
    网络编程基础【day09】:socket解决粘包问题之MD5(八)
    网络编程基础【day09】:socket接收大数据(五)
    网络编程基础【day09】:socket实现文件发送(六)
  • 原文地址:https://www.cnblogs.com/bvin/p/4466402.html
Copyright © 2011-2022 走看看