Volley深度完全解析

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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值