Volley深度完全解析

来源:互联网 发布:想做淘宝刷单hao360 编辑:程序博客网 时间:2024/06/15 12:01

注:转载或修改转载请在文章开头标明本文地址,笔者重在分享禁止用于其他用途
微信公众号:
这里写图片描述

volley介绍

volley是Google在2013年IO大会上提出的一个开源项目,Google针对请求开发不合理的情况,对网络请求,图片异步加载,图片三级缓存防止OOM,做了一套规范操作。
Volley具有以下优点:

  • 网络请求自动调度
  • 多并发请求网络
  • 通过高速缓存一致性透明磁盘和内存一致性
  • 支持请求优先级
  • 支持取消请求
  • 容易扩展定制,如重试和回退
  • 具有调试和跟踪工具

Volley不适合大文件下载,因为Volley会在解析的过程中在内存中保存所有的响应,如果有这些需求,官方推荐可以使用DownLoadManager
可在gradle中增加依赖
dependencies {
...
compile 'com.android.volley:volley:1.0.0'
}

也可以自行下载Volley设置为library

volley官方地址 https://developer.android.com/training/volley/index.html
配置好编译环境的github https://github.com/THtianhao/android-volley.git
google 2013 IO大会volley介绍
https://www.youtube.com/watch?v=yhv8l9F44qo&feature=player_embedded

Volley原理

Volley的原理
想利用volley发一条请求的流程
创建一条请求,然后将请求加入到RequestQueue中,RequestQueue会将请求分派到缓存线程中,缓存线程会从缓存中去取这条request,如果在缓存中找到就交付Response,如果没有则放入到网络线程中去请求,请求返回后放入缓存并交付Response。

图片描述

Volley的使用

最基本的使用官方已经做了示例,笔者不再过多描述,笔者强烈建议没有用过Volley的人先看一下Volley的示例
参照:
https://developer.android.com/training/volley/simple.html
https://developer.android.com/training/volley/requestqueue.html
https://developer.android.com/training/volley/request.html
https://developer.android.com/training/volley/request-custom.html

Volley源码解析

笔者花了一些时间做了一副Volley的脑图,从图中大家体会一下Volley的整体流程
volley脑图

Request

根据Volley流程首先我们需要一条Request

    public Request(int method, String url, Response.ErrorListener listener) {        mMethod = method;        mUrl = url;        mErrorListener = listener;        setRetryPolicy(new DefaultRetryPolicy());        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);    }

以上是构造方法,我们会传递过来一个ErrorListener来传递响应失败的回调
setRetryPolicy主要用于设置一些重试的策略,如失败重试几次
mDefaultTrafficStatsTag根据URL生成用于Request类的重写toString方法来表示一个唯一的request
构造方法传递进来的method表示网络请求类型在Request类中定义

    /**     * Supported request methods.     */    public interface Method {        int DEPRECATED_GET_OR_POST = -1;        int GET = 0;        int POST = 1;        int PUT = 2;        int DELETE = 3;        int HEAD = 4;        int OPTIONS = 5;        int TRACE = 6;        int PATCH = 7;    }

Request类中的另一个重要的方法

 /**     * Notifies the request queue that this request has finished (successfully or with error).     *     * <p>Also dumps all events from this request's event log; for debugging.</p>     */    void finish(final String tag) {        if (mRequestQueue != null) {            mRequestQueue.finish(this);        }        if (MarkerLog.ENABLED) {            final long threadId = Thread.currentThread().getId();            if (Looper.myLooper() != Looper.getMainLooper()) {                // If we finish marking off of the main thread, we need to                // actually do it on the main thread to ensure correct ordering.                Handler mainThread = new Handler(Looper.getMainLooper());                mainThread.post(new Runnable() {                    @Override                    public void run() {                        mEventLog.add(tag, threadId);                        mEventLog.finish(this.toString());                    }                });                return;            }            mEventLog.add(tag, threadId);            mEventLog.finish(this.toString());        }    }

这个方法主要是通知请求结束,交付response
主要操作是mRequestQueue.finish(this);其他的操作主要是为了打一些log
作用是告诉RequestQueue当前的Request请求结束了

再看一下Reqeus的定义

public abstract class Request<T> implements Comparable<Request<T>>

Request是一个抽象类,我们要用Request类必须继承它,并将表明Request的范型(范型的主要作用是提高代码复用率,相比于Object的好处是减少了强制类型转化,防止编译器报错)

要Request必须实现Request的两个抽象方法

abstract protected void deliverResponse(T response);

以上会在ResponseDeliveryRunnable中提及

abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

以上会在Dispacther中会提及,主要用作解析返回的网络请求

RequestQueue

首先看一下Volley类里面默认创建的RequestQueue,里面的东西会在RequestQueue中讲到

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);        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));            }        }        Network network = new BasicNetwork(stack);        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);        queue.start();        return queue;    }

RequestQueue是Volley的核心,基本上所有与Volley主要的类都会在它当中出现
首先来看构造方法

public RequestQueue(Cache cache, Network network, int threadPoolSize,            ResponseDelivery delivery) {        mCache = cache;        mNetwork = network;        mDispatchers = new NetworkDispatcher[threadPoolSize];        mDelivery = delivery;    }

Cache用于存储所有的Request,当然细心的人会发现,每一条request可以设置shouldCache()方法,来表明这一条Request是否需要在Cache里面进行存储。
NetWork 用与网络请求,也是需要在开始的时候创建并传入RequestQueue
threadPoolSize是Volley的网络请求线程数量,Volley默认四条:

/** Number of network request dispatcher threads to start. */    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

ResponseDelivery 是网络请求的交付类,如不指定,在另一条构造方法会创建

 public RequestQueue(Cache cache, Network network, int threadPoolSize) {        this(cache, network, threadPoolSize,                new ExecutorDelivery(new Handler(Looper.getMainLooper())));    }

以上是构造方法的简介

Request的start方法

public void start() {        stop();          mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);        mCacheDispatcher.start();        for (int i = 0; i < mDispatchers.length; i++) {            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,                    mCache, mDelivery);            mDispatchers[i] = networkDispatcher;            networkDispatcher.start();        }    }

start方法是新建RequestQueue后必须执行的方法,里面创建了两种Dispather,一种是Cache(一个),一种是NetWork(默认四个),分别用于存储和网络请求。他们都继承于Thread所以在创建的时候就将这四个线程start

前面都是简单的介绍类,下面我们顺着一条请求来理解接下来的方法
新建一条Request,新建一个RequestQueue,然后将请求add到RequestQueue中

RequestQueue的Add方法

public <T> Request<T> add(Request<T> request) {        // 标记当前request的        request.setRequestQueue(this);        synchronized (mCurrentRequests) {            mCurrentRequests.add(request);        }        // 标记reqeustQueue的number,自增长(++)        request.setSequence(getSequenceNumber());        request.addMarker("add-to-queue");        // 如果request是不可存储的,则跳过Cache queue加入到Net WorkQueue        if (!request.shouldCache()) {            mNetworkQueue.add(request);            return request;        }        // 如果请求早已在请求状态中,则将它们放入到waittingQueue中        synchronized (mWaitingRequests) {            String cacheKey = request.getCacheKey();            if (mWaitingRequests.containsKey(cacheKey)) {                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);                if (stagedRequests == null) {                    stagedRequests = new LinkedList<Request<?>>();                }                stagedRequests.add(request);                mWaitingRequests.put(cacheKey, stagedRequests);                if (VolleyLog.DEBUG) {                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);                }            } else {                // 插入null到waitingRequest中,并放入CacheQueu中去请求                mWaitingRequests.put(cacheKey, null);                mCacheQueue.add(request);            }            return request;        }    }

add方法比较长,我加入了中文注释
主要的作用是将请求放入到各种集合里面,如果不需要存储就放入mNetworkQueue.add(request);中
这里提到几个合集
主要是mWaitingRequests和mCacheQueue
waitingRequest主要用于处理已经在请求状态的请求,可以看到最后几行如果waiting中没有这条request,则放一个null的进去,那么下一次再add的时候waitingRequests中就有了这条request。
mCacheQueue的作用用于网络请求那么既然是网络请求为什么叫mCacheQueue呢?这就要说到上面所讲到start的CacheDispatcher线程了。

CacheDispatcher

再次说明这是一条线程:

public class CacheDispatcher extends Thread

那么有线程就有Run

@Override    public void run() {        if (DEBUG) VolleyLog.v("start new dispatcher");        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        mCache.initialize();        while (true) {            try {                // 从cache中取一条请求                final Request<?> request = mCacheQueue.take();                request.addMarker("cache-queue-take");                // 如果请求cacel()则直接交付                if (request.isCanceled()) {                    request.finish("cache-discard-canceled");                    continue;                }                // 试图从请求中取出数据,如果没有则放入网络队列                Cache.Entry entry = mCache.get(request.getCacheKey());                if (entry == null) {                    request.addMarker("cache-miss");                    // Cache miss; send off to the network dispatcher.                    mNetworkQueue.put(request);                    continue;                }                //如果超时,则放入到网络队列                if (entry.isExpired()) {                    request.addMarker("cache-hit-expired");                    request.setCacheEntry(entry);                    mNetworkQueue.put(request);                    continue;                }                //我们的缓存命中了,将请求的数据解析交付到Request                request.addMarker("cache-hit");                Response<?> response = request.parseNetworkResponse(                        new NetworkResponse(entry.data, entry.responseHeaders));                request.addMarker("cache-hit-parsed");                if (!entry.refreshNeeded()) {                    // 不完全过期,直接交付                    mDelivery.postResponse(request, response);                } else {                    // 缓存软过期,我们直接交付                    // 但是我们也需要放入网络中去请求                    request.addMarker("cache-hit-refresh-needed");                    request.setCacheEntry(entry);                    response.intermediate = true;                    mDelivery.postResponse(request, response, new Runnable() {                        @Override                        public void run() {                            try {                                mNetworkQueue.put(request);                            } catch (InterruptedException e) {                            }                        }                    });                }            } catch (InterruptedException e) {                if (mQuit) {                    return;                }                continue;            }        }    }

结合翻译后的注视可以看出来,其实就是从mCacheQueue中取这条请求,如果缓存中有这条请求,就去交付,如果没有或过期就放倒mNetworkQueue中去,那么mNetworkQueue里面的东西在哪处理?还记得开头新建的四个线程吗?
对,就是NetworkDispatcher

NetworkDispatcher

    public BasicNetwork(HttpStack httpStack) {        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));    }

构造方法里面传入了一个httsStack 这个类是具体操作网络请求的类
在Volley类里面,默认会创建一个HurlStack

@Override    public void run() {        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        while (true) {            long startTimeMs = SystemClock.elapsedRealtime();            Request<?> request;            try {                // 从mQueue中拿一条Request出来(mQueue就是NetWorkQueue)                request = mQueue.take();            } catch (InterruptedException e) {                if (mQuit) {                    return;                }                continue;            }            try {                request.addMarker("network-queue-take");                // 如果request设置了cancel那么则直接交付                if (request.isCanceled()) {                    request.finish("network-discard-cancelled");                    continue;                }                addTrafficStatsTag(request);                // 真正进行请求网络                NetworkResponse networkResponse = mNetwork.performRequest(request);                request.addMarker("network-http-complete");                // 如果返回304或者已经交付了的话就直接finish                if (networkResponse.notModified && request.hasHadResponseDelivered()) {                    request.finish("not-modified");                    continue;                }                //网络请求解析后的地方                Response<?> response = request.parseNetworkResponse(networkResponse);                request.addMarker("network-parse-complete");                // 如果不设置shouldCache,默认的shouldCache为true写入到cache中                if (request.shouldCache() && response.cacheEntry != null) {                    mCache.put(request.getCacheKey(), response.cacheEntry);                    request.addMarker("network-cache-written");                }                // 将请求交付                request.markDelivered();                mDelivery.postResponse(request, response);            } catch (VolleyError volleyError) {                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);                parseAndDeliverNetworkError(request, volleyError);            } catch (Exception e) {                VolleyLog.e(e, "Unhandled exception %s", e.toString());                VolleyError volleyError = new VolleyError(e);                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);                mDelivery.postError(request, volleyError);            }        }    }

首先先从需要进行网络请求的Request拿出来,然后进行请求,并存入cache中。
在这里可以看到Volley的网络请求是在mNetwork.performRequest(request);进行的。请求后会执行的request.parseNetworkResponse(networkResponse);这个方法在之前说过,是继承Reqeust必须重载的方法,在里面做一些自定义的解析。
mNetWrok是什么?就是创建RequestQueue的时候传递进来的Network,在Volley中,有两个Network供人们使用。当然你也可以自定义
前面说的Network是:

BasicNetwork

@Override    public NetworkResponse performRequest(Request<?> request) throws VolleyError {        long requestStart = SystemClock.elapsedRealtime();        while (true) {            HttpResponse httpResponse = null;            byte[] responseContents = null;            Map<String, String> responseHeaders = Collections.emptyMap();            try {                // 网络请求并返回返回数据                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.                //如果response是304则直接返回                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {                    Entry entry = request.getCacheEntry();                    if (entry == null) {                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,                                responseHeaders, true,                                SystemClock.elapsedRealtime() - requestStart);                    }                    // A HTTP 304 response does not have all header fields. We                    // have to use the header fields from the cache entry plus                    // the new ones from the response.                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5                    //Http返回值为304不包含所有的头,我们需要从加上cache里面的头生成一个新的头去返回                    entry.responseHeaders.putAll(responseHeaders);                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,                            entry.responseHeaders, true,                            SystemClock.elapsedRealtime() - requestStart);                }                // Some responses such as 204s do not have content.  We must check.                //一些response没有上下文例如204,我们需要检查一下                if (httpResponse.getEntity() != null) {                  responseContents = entityToBytes(httpResponse.getEntity());                } else {                  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();                }                //返回网络请求                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,                        SystemClock.elapsedRealtime() - requestStart);            } 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) {                //处理反返回码异常的重试,由于整体是循环,所以失败后会重试                int statusCode;                if (httpResponse != null) {                    statusCode = httpResponse.getStatusLine().getStatusCode();                } else {                    throw new NoConnectionError(e);                }                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());                NetworkResponse networkResponse;                if (responseContents != null) {                    networkResponse = new NetworkResponse(statusCode, responseContents,                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||                            statusCode == HttpStatus.SC_FORBIDDEN) {                        attemptRetryOnException("auth",                                request, new AuthFailureError(networkResponse));                    } else if (statusCode >= 400 && statusCode <= 499) {                        // Don't retry other client errors.                        throw new ClientError(networkResponse);                    } else if (statusCode >= 500 && statusCode <= 599) {                        if (request.shouldRetryServerErrors()) {                            attemptRetryOnException("server",                                    request, new ServerError(networkResponse));                        } else {                            throw new ServerError(networkResponse);                        }                    } else {                        // 3xx? No reason to retry.                        throw new ServerError(networkResponse);                    }                } else {                    attemptRetryOnException("network", request, new NetworkError());                }            }        }    }

以上主要是做了一些对http请求返回后是否需要重试的处理,如果需要重试,则会抛出异常来继续循环请求如果不需要重试则return new NetworkResponse
我们看到Response是在
httpResponse = mHttpStack.performRequest(request, headers);返回的
那么构造方法里面的传入HurlStack的performRequest这里面做了什么?

HurlStack

@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);        if (mUrlRewriter != null) {            String rewritten = mUrlRewriter.rewriteUrl(url);            if (rewritten == null) {                throw new IOException("URL blocked by rewriter: " + url);            }            url = rewritten;        }        URL parsedUrl = new URL(url);        //通过url和request的一些超时数据放入connect        HttpURLConnection connection = openConnection(parsedUrl, request);        //将头放入connect        for (String headerName : map.keySet()) {            connection.addRequestProperty(headerName, map.get(headerName));        }        //设置connent的Method如果没有指定,并且有body则是post        //设置connent的Method如果没有指定,并且没有body则是get        setConnectionParametersForRequest(connection, request);        // Initialize HttpResponse with data from the HttpURLConnection.        //从HttpURLConnection初始化httpResponse和数据        //设置协议版本        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);        int responseCode = connection.getResponseCode();        if (responseCode == -1) {            // 如果response不能被取回,则返回-1            throw new IOException("Could not retrieve response code from HttpUrlConnection.");        }        StatusLine responseStatus = new BasicStatusLine(protocolVersion,                connection.getResponseCode(), connection.getResponseMessage());        BasicHttpResponse response = new BasicHttpResponse(responseStatus);        //http的类型不是head并且除去特定的错误码,将connection的数据存放到response里面        if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {            response.setEntity(entityFromConnection(connection));        }        //将http的头加入到response中        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);            }        }       //返回response        return response;    }

看到这里大家就会发现,Volley默认使用的是HttpURLConnection来进行网络请求的。
这个方法里面主要做的操作就是根据传递过来的url和head来进行网络请求
其他的地方加注释

看到这里的时候大家基本对Volley的网络请求有了一定的了解,那么请求过后的response是如何交付的呢?可以回到cache和network的Dispatcher里面看看,里面都有网络的交付,我们从NetworkDispatcher中去看:

mDelivery.postResponse(request, response);

请求回来后会通过mDelivery来交付

public NetworkDispatcher(BlockingQueue<Request<?>> queue,            Network network, Cache cache,            ResponseDelivery delivery) {        mQueue = queue;        mNetwork = network;        mCache = cache;        mDelivery = delivery;    }

NetworkDispatcher的构造方法里面会传入ResponseDelivery再往回看:

    public RequestQueue(Cache cache, Network network, int threadPoolSize) {        this(cache, network, threadPoolSize,                new ExecutorDelivery(new Handler(Looper.getMainLooper())));    }

我们找到了Request的构造方法,可以看到在里面创建了一个ExecutorDelivery并传入了主线程的looper

ExecutorDelivery

那么我们用于请求交付的地方就是这里了

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

构造方法里面传递进一个handler,从前面可以看到post到了主线程用于交付
下面是我们在dispatcher中看到的postResponse

 @Override    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {        request.markDelivered();        request.addMarker("post-response");        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));    }

这里对标记为已经delivered并执行了一个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() {            //如果request被标记为cancel状态,则直接finish            if (mRequest.isCanceled()) {                mRequest.finish("canceled-at-delivery");                return;            }            // 如果成功则交付到request的deliverResponse            if (mResponse.isSuccess()) {                mRequest.deliverResponse(mResponse.result);            } else {                mRequest.deliverError(mResponse.error);            }            // 如果request是intermediate状态,打一个log如果不是则finish            if (mResponse.intermediate) {                mRequest.addMarker("intermediate-response");            } else {                mRequest.finish("done");            }            // 如果我们有一个runnable可能是postdelay,执行它            if (mRunnable != null) {                mRunnable.run();            }       }    }

那么总而言之,response通过层层传递,又回到了reqeust中并且交付后执行了request的finish方法,不知是否还记得request的finish方法调用了RequestQueue的finish方法,里面又做了什么呢:

RequestQueue中的finish方法

<T> void finish(Request<T> request) {        // 从mCurrentRequests中移除(在add的时候会添加,所以只要没有finish的request都会存在里面)        synchronized (mCurrentRequests) {            mCurrentRequests.remove(request);        }        //通知mFinishedListeners(这个接口也是可以在创建request的时候对其进行设置的,通过流程可以知道,如果我们有业务逻辑需要在请求finish后执行的话,可以进行设置此监听)        synchronized (mFinishedListeners) {          for (RequestFinishedListener<T> listener : mFinishedListeners) {            listener.onRequestFinished(request);          }        }        if (request.shouldCache()) {            synchronized (mWaitingRequests) {                String cacheKey = request.getCacheKey();                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);                if (waitingRequests != null) {                    if (VolleyLog.DEBUG) {                        VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",                                waitingRequests.size(), cacheKey);                    }                    mCacheQueue.addAll(waitingRequests);                }            }        }    }

前面的东西很好理解,后面的呢?
从RequestQueue的add方法中我们知道mWaitingRequests,相同url进行请求的request会放入mWaitingRequests中,这里的操作是将mWaitingRequests中所有相同url(key)的request取出,然后全部放入mCacheQueue中,为什么要这么做?因为这时候第一条request已经请求完成,所以在cache中已经存在,所以可以直接取出直接交付(忘记的同学可以翻一翻前面)。

以上就是进行Volley进行网络请求的所有流程了

----------------------------------
深究一下,mWaitingRequests中存放的健值对的key是url,实际存储到cache里面的也是url吗?分析一下chache,volley默认使用的是DiskBasedCache类

DiskBasedCache

private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;public DiskBasedCache(File rootDirectory) {        this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);    }

DiskBasedCache默认有一个5M的空间
我们主要关心cache的存取

public synchronized void put(String key, Entry entry) {        //首先判断加入后缓存是否过大,如果过大则随机删除一些数据,知道缓存够用        pruneIfNeeded(entry.data.length);        //        File file = getFileForKey(key);        try {            BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));            CacheHeader e = new CacheHeader(key, entry);            boolean success = e.writeHeader(fos);            if (!success) {                fos.close();                VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());                throw new IOException();            }            fos.write(entry.data);            fos.close();            putEntry(key, e);            return;        } catch (IOException e) {        }        boolean deleted = file.delete();        if (!deleted) {            VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());        }    }

在这个同步方法里面,首先传入的是是将url和response返回的数据,然后根据url生成一个文件,并将url和Entry传入到CacheHeader中,把CacheHeader写入到文件。那么文件名是不是就是url

private String getFilenameForKey(String key) {        int firstHalfLength = key.length() / 2;        String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());        localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());        return localFilename;    }

我反复的强调想必大家也猜到了,文件名不是url是根据一套规则(前半段和后半段)生成的一个hashcode。这就是存在我们Cache中的文件了

关于Volley的图片缓存在另一篇博客中写

至此Volley的解析也就完成了,从每个流程都对volley进行了分析,如果大家还有不明白的可以下载源码进行分析,有一些细节我没有讲,可以自己多看看,我将配置好的环境放到了git上面大家可以直接下载
https://github.com/THtianhao/android-volley.git
我意在分享模块化不分享碎片内容,如果有错误的地方大家可以指出,可以在评论多提建议

2 0