• Volley简单学习使用五—— 源代码分析三


    一、Volley工作流程图:


    二、Network
        在NetworkDispatcher中须要处理的网络请求。由以下进行处理:
        NetworkResponse networkResponse = mNetwork.performRequest(request);
    看一下mNetwork的定义:(定义在NetworkDispatcher中)

        /** The network interface for processing requests. */
        private final Network mNetwork;
    NetworkDispatcher.mNetwork初始化发生在RequestQueue.start()中:

        NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
             mCache, mDelivery);
    而RequestQueue.mNetwork是在其构造函数中传入的:

        public RequestQueue(Cache cache, Network network, int threadPoolSize,
                ResponseDelivery delivery) {
            mCache = cache;
            mNetwork = network;
            mDispatchers = new NetworkDispatcher[threadPoolSize];
            mDelivery = delivery;
        }
    由前面分析知RequestQueue的构建是在Volley.newRequestQueue中实现的:

        //创建以stack为參数的Network对象
        Network network = new BasicNetwork(stack);
        //创建RequestQueue对象
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();//继续向下分析的入口

        能够看出mNetwork事实上是BasicNetwork对象。
        则NetworkResponse中mNetwork实际上调用的是BasicNetwork.performRequest(),这是一个专门用来处理网络请求的函数,其作用为调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse
    看一下其源代码:
       @Override
        public NetworkResponse performRequest(Request<?> request) throws VolleyError {
            long requestStart = SystemClock.elapsedRealtime();
            while (true) {
                HttpResponse httpResponse = null;
                byte[] responseContents = null;
                Map<String, String> responseHeaders = new HashMap<String, String>();
                try {
                  /** 忽略网络处理的细节*/
                    // Gather headers.
                    Map<String, String> headers = new HashMap<String, String>();
                    addCacheHeaders(headers, request.getCacheEntry());
                   
                    /**运行网络请求
                     * 这里调用了HttpStack.performRequest。并得到一个HttpResponse返回结果*/
                    httpResponse = mHttpStack.performRequest(request, headers);
                   
                    StatusLine statusLine = httpResponse.getStatusLine();
                    int statusCode = statusLine.getStatusCode();
                    responseHeaders = convertHeaders(httpResponse.getAllHeaders());
     
                    /**新奇度验证:
                     * 304 Not Modified:client有缓冲的文件并发出了一个条件性的请求
                     * (通常是提供If-Modified-Since头表示客户仅仅想比指定日期更新的文档)。
                     * server告诉客户。原来缓冲的文档还能够继续使用。*/
                    if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                      /** 解析成NetworkResponse,返回*/
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                                request.getCacheEntry().data, responseHeaders, true);
                    }
     
                    // 推断responses是否有实体信息,一些响应如204,并不包括content。所以须要验证
                    if (httpResponse.getEntity() != null) {
                      //实体信息转化成byte[]
                        responseContents = entityToBytes(httpResponse.getEntity());
                    } else {
                      // 无实体信息情况
                      responseContents = new byte[0];
                    }
     
                    // 超时情况处理.
                    long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                    logSlowRequests(requestLifetime, request, responseContents, statusLine);
     
                    if (statusCode < 200 || statusCode > 299) {
                        throw new IOException();
                    }
                    return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
                } catch (SocketTimeoutException e) {
                    attemptRetryOnException("socket", request, new TimeoutError());
                } catch (ConnectTimeoutException e) {
                    attemptRetryOnException("connection", request, new TimeoutError());
                } catch (MalformedURLException e) {
                    throw new RuntimeException("Bad URL " + request.getUrl(), e);
                } catch (IOException e) {
                    ...
                }
            }
        }

     总结一下Network.performRequest所做的工作:

    1、由传入的HttpStack对象运行网络请求:mHttpStack.performRequest()

    2、解析响应结果,将HttpResponse解析成NetworkResponse;

    3、对返回结果进行新奇度验证(304)

    4、将response的实体信息转化为byte数组

    5、超时情况处理,假设发生超时,认证失败等错误。进行重试操作(attemptRetryOnException)。直到成功、抛出异常(不满足重试策略等)结束。


    attemptRetryOnException()是依据重试策略进行请求重试操作:

        /**
         * Attempts to prepare the request for a retry. If there are no more attempts remaining in the
         * request's retry policy, a timeout exception is thrown.
         */
        private static void attemptRetryOnException(String logPrefix, Request<?

    > request, VolleyError exception) throws VolleyError { RetryPolicy retryPolicy = request.getRetryPolicy(); int oldTimeout = request.getTimeoutMs(); try { retryPolicy.retry(exception); } catch (VolleyError e) { request.addMarker( String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout)); throw e; } request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout)); }

    三、HttpClientStack、HurlStack
        据上面源代码知。网络请求处理的逻辑实际上是交由传进来的參数HttpStack进行处理。

    前面已经分析过。Android2.3之前使用 HttpClientStack,之后使用HurlStack。

    1、先看两者的父类HttpStack:
        public interface HttpStack {
            /**
             * Performs an HTTP request with the given parameters.
             * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
             * and the Content-Type header is set to request.getPostBodyContentType().</p>
             * @param request the request to perform
             * @param 发起请求之前,加入额外的请求 Headers {@link Request#getHeaders()}
             */
            public HttpResponse performRequest(Request<?

    > request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError; }

    2、HttpClientStack(使用HttpClient来实现)

        @Override
        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);// Nothing.空函数,用于重写;该函数在request被excute之前被调用 //一些网络设置 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); }

    附一:createHttpRequest函数:

        /**
         * 依据传进来的request来构造合适的HttpUriRequest
         */
        static HttpUriRequest createHttpRequest(Request<?> request,
                Map<String, String> additionalHeaders) throws AuthFailureError {
            switch (request.getMethod()) {
                case Method.DEPRECATED_GET_OR_POST: {
                    // This is the deprecated way that needs to be handled for backwards compatibility.
                    // If the request's post body is null, then the assumption is that the request is
                    // GET.  Otherwise, it is assumed that the request is a POST.
                    byte[] postBody = request.getPostBody();
                    if (postBody != null) {
                        HttpPost postRequest = new HttpPost(request.getUrl());
                        postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
                        HttpEntity entity;
                        entity = new ByteArrayEntity(postBody);
                        postRequest.setEntity(entity);
                        return postRequest;
                    } else {
                        return new HttpGet(request.getUrl());
                    }
                }
                /***********一般较多使用的是POST与GET。其等同于HttpClient的一般使用流程***************/
                case Method.GET:
                    return new HttpGet(request.getUrl());
                case Method.DELETE:
                    return new HttpDelete(request.getUrl());
                case Method.POST: {
                    HttpPost postRequest = new HttpPost(request.getUrl());
                    //这里就看到了前面实现Request时,重写getBodyContentType()函数的意义
                    postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                    setEntityIfNonEmptyBody(postRequest, request);
                    return postRequest;
                }
                case Method.PUT: {
                    HttpPut putRequest = new HttpPut(request.getUrl());
                    putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                    setEntityIfNonEmptyBody(putRequest, request);
                    return putRequest;
                }
                default:
                    throw new IllegalStateException("Unknown request method.");
            }
        }
    3、HurlStack(由HttpURLConnection来实现)
        @Override
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
                throws IOException, AuthFailureError {
            String url = request.getUrl();
            HashMap<String, String> map = new HashMap<String, String>();
            map.putAll(request.getHeaders());
            map.putAll(additionalHeaders);
            //UrlRewriter见附一
            if (mUrlRewriter != null) {
                String rewritten = mUrlRewriter.rewriteUrl(url);
                if (rewritten == null) {
                    thrownew IOException("URL blocked by rewriter: " + url);
                }
                url = rewritten;
            }
            /**************HttpURLConnection的一般使用流程*******************/
            URL parsedUrl = new URL(url);
            HttpURLConnection connection = openConnection(parsedUrl, request);
            for (String headerName : map.keySet()) {
                connection.addRequestProperty(headerName, map.get(headerName));
            }
            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.
                thrownew 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;
        }
    附一:UrlRewriter

        /** 对URLs在使用前进行重写转换*/
        public interface UrlRewriter {
            /**
             * Returns a URL to use instead of the provided one, or null to indicate
             * this URL should not be used at all.
             */
            public String rewriteUrl(String originalUrl);
        }
    參数mUrlRewriter通过HttpStack的构造函数传入进来,故能够自行进行定义:
        public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
            mUrlRewriter = urlRewriter;
            mSslSocketFactory = sslSocketFactory;
        }

    四、NetworkResponse
        回到起点NetworkDispatcher(Thread)中的run()函数,当中:

        NetworkResponse networkResponse = mNetwork.performRequest(request);

    以下继续看NetworkResponse的源代码:
    NetworkResponse类非常easy,仅是用以在多个类中传递数据,其成员变量:
    1)成员变量
    int statusCode Http 响应状态码

    byte[] data Body 数据
    Map<String, String> headers 响应 Headers
    boolean notModified 表示是否为 304 响应
    long networkTimeMs 请求耗时

    2)其主体仅仅为几个构造函数:
        public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
                boolean notModified) {
            this.statusCode = statusCode;
            this.data = data;
            this.headers = headers;
            this.notModified = notModified;
        }
     
        public NetworkResponse(byte[] data) {
            this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
        }
     
        public NetworkResponse(byte[] data, Map<String, String> headers) {
            this(HttpStatus.SC_OK, data, headers, false);
        }
    3)回想一下前面分析的设计NetworkResponse的类之间数据的传递关系:



    这里的主体是依据NetworkDispatcher.run()函数进行分析的
    0、函数中调用Network.performRequest();
         NetworkResponse networkResponse = mNetwork.performRequest(request);
         而Network.performRequest()是基于HttpStack实现的;
    1、HttpClientStack与HurlStack(分别基于HttpClient与HttpURLConnection实现)中的public HttpResponse performRequest()函数返回HttpResponse ;
    2、Network(实际为BasicNetwork)中performRequest()方法。使用1中的两个HttpStack类。获取到其返回值HttpResponse,然后将其解析成为NetworkResponse;
    3、Request中 abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
        将NetworkResponse解析成Response;
        而该函数的调用是在NetworkDispatcher中的run()函数中调用的。                
    4、在NetworkDispatcher.run()的最后一步:
        mDelivery.postResponse(request, response);
        将response传递给了ResponseDelivery
    后面继续看Delivery的逻辑;
    ResponseDelivery mDelivery的实际类型是ExecutorDelivery:
    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }
    public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    
    }
    能够看到非常easy,就是使用主线程的Looper构建一个Handler。以下全部的post操作都是调用这个Handler来运行Runnable;

    比方:

    @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }
    将传递来的Response转化为ResponseDeliveryRunnable ,显然这是一个Runnable;

    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;
    
        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
            mRequest = request;
            mResponse = response;
            mRunnable = runnable;
        }
    
        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            // If this request has canceled, finish it and don't deliver.
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }
    
            // Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }
    
            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }
    
            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }
    
    }
    在这个子线程中,转而调用 Request来deliverResponse:

    以StringRequest为例,来看这个函数:

    @Override
    protected void deliverResponse(String response) {
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }
    这个Listener就是自己在定义Request的时候声明的ResponseListener,能够看到这个Listener工作在子线程中,所以假设要更新界面,注意使用Handler把消息传递主线程进行处理。


    ***************************************************** Volley图片载入的实现 *******************************************************
    Volley的图片载入主要还是基于上面的原理来实现的。详细例如以下:
    ImageLoader的使用:
    //创建ImageLoader
    imageLoader = new ImageLoader(httpUtils.getRequestQueue(), imageCache);
    public ImageLoader(RequestQueue queue, ImageCache imageCache) {
        mRequestQueue = queue;
        mCache = imageCache;
    
    }
    这里面的ImageCache是自己定义的:
    // 获取最大内存缓存大小
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    MAX_CACHE_SIZE = maxMemory / 8; // 定义为应用最大缓存的1/8
    
    mImageLruCache = new LruCache<String, Bitmap>(MAX_CACHE_SIZE){
        @Override
        protected int sizeOf(String url, Bitmap bitmap){
            return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
        }
    
    };
    
    // 创建ImageCache
    imageCache = new ImageLoader.ImageCache() {
        @Override
        public void putBitmap(String url, Bitmap bitmap) {
            mImageLruCache.put(url, bitmap);
        }
    
        @Override
        public Bitmap getBitmap(String url) {
            return mImageLruCache.get(url);
        }
    };
    使用LruCache来实现ImageCache接口,实现图片的内存缓存:
    public interface ImageCache {
        public Bitmap getBitmap(String url);
        public void putBitmap(String url, Bitmap bitmap);
    }
    载入图片时的使用方法:

    imageListener = ImageLoader.getImageListener(myImageView, default_pg, failed_pg);
    imageLoader.get(imageUrl, imageListener);
    来到ImageLoader#get:

    public ImageContainer get(String requestUrl, final ImageListener listener) {
        return get(requestUrl, listener, 0, 0);
    
    }
    
    public ImageContainer get(String requestUrl, ImageListener imageListener,
                              int maxWidth, int maxHeight) {
        return get(requestUrl, imageListener, maxWidth, maxHeight, ImageView.ScaleType.CENTER_INSIDE);
    
    }
    
    public ImageContainer get(String requestUrl, ImageListener imageListener,
                              int maxWidth, int maxHeight, ImageView.ScaleType scaleType) {
    
        // 假设操作不是在主线程,则直接抛出异常
        throwIfNotOnMainThread();
    
        // 为图片的URL创建一个特定的cacheKey,注意这个cache还和图片的大小及scaleType相关
        final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
    
        // 这里会使用自己定义的LruCache去获取一个Bitmap实例
        Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
        // 假设缓存中已经存在。则直接返回
        if (cachedBitmap != null) {
            // Return the cached bitmap.
            ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
            imageListener.onResponse(container, true);
            return container;
        }
    
        // 假设缓存中不存在,则进行获取
        ImageContainer imageContainer =
                new ImageContainer(null, requestUrl, cacheKey, imageListener);
    
        // 通知Observer这时能够使用默认的图片
        imageListener.onResponse(imageContainer, true);
    
        // 推断是否已经有了一个同样的请求在等待
        BatchedImageRequest request = mInFlightRequests.get(cacheKey);
        if (request != null) {
            // If it is, add this request to the list of listeners.
            request.addContainer(imageContainer);
            return imageContainer;
        }
    
        // 创建一个Request,反复之前的流程
        Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
                cacheKey);
    
        mRequestQueue.add(newRequest);
        mInFlightRequests.put(cacheKey,
                new BatchedImageRequest(newRequest, imageContainer));
        return imageContainer;
    }
    处理逻辑大致和前面的addRequest同样。首先推断缓存中是否已经存在该url相应的bitmap。假设存在直接返回;假设不存在,先推断是否已经有了一个同样的请求在等待。假设是,把这个请求加入到监听者链表中。假设不存在,则创建一个Request<Bitmap>,加入到RequestQueue中,从网络中去获取;从网络中获取的流程和前面分析的同样。


    先来看Request<Bitmap>:

    protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,
            ScaleType scaleType, final String cacheKey) {
        return new ImageRequest(requestUrl, new Listener<Bitmap>() {
            @Override
            public void onResponse(Bitmap response) {
                onGetImageSuccess(cacheKey, response);
            }
        }, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                onGetImageError(cacheKey, error);
            }
        });
    }
    实际上返回一个ImageRequest类型。来看其请求成功的响应:即把获得的图片存储到缓存中。

    protected void onGetImageSuccess(String cacheKey, Bitmap response) {
        // 把获取到的图片存储到缓存中
        mCache.putBitmap(cacheKey, response);
        // 能够看到假设是多个同样请求在等待,则能够同一时候进行更新处理
        BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
    
        if (request != null) {
            // Update the response bitmap.
            request.mResponseBitmap = response;
            // Send the batched response
            batchResponse(cacheKey, request);
        }
    }
    最后NetWork运行的结果会封装成NetWorkResponse。通过ResponseDelivery进行转发,这个类最后会调用Request中deliverResponse方法:

    @Override
    protected void deliverResponse(Bitmap response) {
        mListener.onResponse(response);
    }
    这个Listener就是最初定义的ImageListener:

    public static ImageListener getImageListener(final ImageView view,
            final int defaultImageResId, final int errorImageResId) {
        return new ImageListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                if (errorImageResId != 0) {
                    view.setImageResource(errorImageResId);
                }
            }
    
            @Override
            public void onResponse(ImageContainer response, boolean isImmediate) {
                if (response.getBitmap() != null) {
                    view.setImageBitmap(response.getBitmap());
                } else if (defaultImageResId != 0) {
                    view.setImageResource(defaultImageResId);
                }
            }
        };
    }

    能够看到这里终于给View空间设置了图片,以上就是Volley实现图片载入的流程。

  • 相关阅读:
    浅析 Map 和 WeakMap 区别以及使用场景
    wait 和async,await一起使用引发的死锁问题
    PVE迁移主机出错的解决
    geotools格式转换:CSV2Shp
    gdal in java
    使用Java Swing和GeoTools开发桌面GIS软件
    bricscad是什么?geotools for bricscad介绍
    windows 设置bat程序开机自启动
    geotools:org.geotools.feature.SchemaException: Error decoding srs: 4326
    geotools之datastore(数据存储)
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/6945305.html
Copyright © 2020-2023  润新知