Volley 源码分析

前言

Volley是Google推出的一款网络请求框架,虽然已经很久了,但还是想想它的源码。Volley的网络请求实现,在Android 2.3以上使用的是HttpURLConnection,而2.3以下则是使用HttpClient实现。

简单使用

注意:在Android 6.0时,谷歌去掉了HttpClient的类,而Volley依赖HttpClient,所以需要进行配置才行

在模块的build.gradle中,添加

android {
    useLibrary 'org.apache.http.legacy'
}

在AndroidManifest.xml中,也需要配置


    

还有别忘了,在AndroidManifest.xml中,添加网络权限喔


    

在Android 9.0时,不允许明文请求,还需要在清单的application节点中,进行配置,允许明文请求



先来看下GET请求,Volley是怎么发出的吧

一般为3个步骤:

  • 创建RequestQueue请求队列
  • 创建请求,设置成功和失败的回调
  • 把请求加入队列,发出请求
 //1.创建请求队列
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
//2.创建请求,设置回调
StringRequest request = new StringRequest("https://www.wanandroid.com/article/list/1/json", new Response.Listener() {
    @Override
    public void onResponse(String response) {
        //请求成功
        Log.d(TAG, response);
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        error.printStackTrace();
        //请求失败
        Log.e(TAG, error.getMessage(), error);
    }
});
//3.加入队列,发起请求
requestQueue.add(request);

开始分析

先从创建RequestQueue请求队列开始,它是调用Volley.newRequestQueue(context),来创建的,所以先来看看Volley类

Volley

newRequestQueue()有3个重载方法,最终都是调用3个参数的newRequestQueue(context, stack, maxDiskCacheBytes),里面主要是做的是:

  • 确定缓存目录
  • 确定UA
  • 根据当前Android版本号,在Android 2.3及其以上,创建实现HurlStack,它是HttpURLConnection实现的,2.3以下则创建实现HttpClientStack,它是HttpClient实现的
  • 创建网络操作实现,Network是一个操作接口,具体实现类是BasicNetwork,它需要传入HttpStack
  • 创建RequestQueue,创建DiskBasedCache并传入,它是一个缓存操作类,它的是Cache接口的具体实现,同时把Network实现传入
  • 最后,调用RequestQueue的start()方法,开启队列
public class Volley {
    /**
     * 默认磁盘缓存目录名
     */
    private static final String DEFAULT_CACHE_DIR = "volley";

    /**
     * 创建请求队列,支持设置请求实现和最大磁盘缓存大小
     *
     * @param stack             请求实现,传null则自动根据版本选择
     * @param maxDiskCacheBytes 最大磁盘缓存大小,传-1则使用默认的5Ms
     */
    public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
        //缓存目录
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        //设置UA
        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;
        //根据传入的磁盘缓存大小,创建请求队列
        if (maxDiskCacheBytes <= -1) {
            //没有设置,使用默认值创建
            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        } else {
            //设置了,则使用指定的值
            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
        }

        //开启队列
        queue.start();
        return queue;
    }

    /**
     * 创建请求队列,支持设置请求实现和最大磁盘缓存大小
     *
     * @param maxDiskCacheBytes 最大磁盘缓存大小
     */
    public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) {
        return newRequestQueue(context, null, maxDiskCacheBytes);
    }

    /**
     * 创建请求队列,支持设置请求实现
     *
     * @param stack 请求实现
     */
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        return newRequestQueue(context, stack, -1);
    }

    /**
     * 创建请求队列
     */
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }
}

RequestQueue

RequestQueue的构造中,除了保存传进来的Cache、Network,还有threadPoolSize以及ResponseDelivery

threadPoolSize就是线程池个数,代表处理请求的线程个数(但Volley并没有使用线程池,而是直接创建指定数量的线程),ResponseDelivery则为响应分发器,用来把请求结果或错误结果分发给我们设置的监听器

上面调用了RequestQueue的start()方法,这个方法中,主要是创建了一个CacheDispatcher和4个NetworkDispatcher,并且调用了他们的start()方法

start()方法中,传入了一个缓存队列,一个网络队列,Cache,ResponseDelivery,其中缓存队列和网络队列都是PriorityBlockingQueue类型,就是优先级阻塞队列,可以按优先级把请求排序,并且阻塞队列在被获取队列元素时,如果队列为空,就会阻塞在那里

/**
 * 请求队列管理类
 */
public class RequestQueue {
    /**
     * 请求完成监听器
     */
    public interface RequestFinishedListener {
        /**
         * 请求完成时,回调
         */
        void onRequestFinished(Request request);
    }

    /**
     * 用于生成请求序列号
     */
    private final AtomicInteger mSequenceGenerator = new AtomicInteger();

    /**
     * Staging area for requests that already have a duplicate request in flight.
     *
     * 
    *
  • containsKey(cacheKey) indicates that there is a request in flight for the given cache * key.
  • *
  • get(cacheKey) returns waiting requests for the given cache key. The in flight request * is not contained in that list. Is null if no requests are staged.
  • *
*/ private final Map>> mWaitingRequests = new HashMap<>(); /** * 正在请求中的请求集合 */ private final Set> mCurrentRequests = new HashSet<>(); /** * 请求的缓存队列,是一个PriorityBlockingQueue,可以根据优先级来出队 */ private final PriorityBlockingQueue> mCacheQueue = new PriorityBlockingQueue<>(); /** * 请求的网络队列,也是一个PriorityBlockingQueue */ private final PriorityBlockingQueue> mNetworkQueue = new PriorityBlockingQueue<>(); /** * 请求分发器的数量,默认为4个 */ private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; /** * 缓存类,用于保存缓存和查询缓存 */ private final Cache mCache; /** * 网络请求执行器 */ private final Network mNetwork; /** * 网络请求结果分发器 */ private final ResponseDelivery mDelivery; /** * 网络请求分发器数组 */ private final NetworkDispatcher[] mDispatchers; /** * 缓存分发器 */ private CacheDispatcher mCacheDispatcher; /** * 网络请求完成后的监听器集合 */ private final List mFinishedListeners = new ArrayList<>(); /** * 创建一个请求队列RequestQueue,要调用 {@link #start()} ,队列才会开始工作 * * @param cache 用于把缓存到磁盘 * @param network 用于执行请求 * @param threadPoolSize 用于设置网络请求分发器的线程池数量 * @param delivery 用于回调请求结果的分发器 */ public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; } /** * 创建一个请求队列RequestQueue,可以指定分发器的个数 */ public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } /** * 创建一个请求队列RequestQueue,使用默认配置 */ public RequestQueue(Cache cache, Network network) { this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); } /** * 让队列开始工作 */ 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(); } } /** * 停止缓存分发器和网络分发器 */ public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (int i = 0; i < mDispatchers.length; i++) { if (mDispatchers[i] != null) { mDispatchers[i].quit(); } } } /** * Gets a sequence number. */ public int getSequenceNumber() { return mSequenceGenerator.incrementAndGet(); } /** * Gets the {@link Cache} instance being used. */ public Cache getCache() { return mCache; } /** * 过滤器接口 */ public interface RequestFilter { //判断是否过滤 boolean apply(Request request); } /** * Cancels all requests in this queue for which the given filter applies. * * @param filter The filtering function to use */ public void cancelAll(RequestFilter filter) { //同步锁 synchronized (mCurrentRequests) { //遍历当前请求集合 for (Request request : mCurrentRequests) { //匹配到了,则取消请求 if (filter.apply(request)) { request.cancel(); } } } } /** * 取消指定tag标记的请求 */ public void cancelAll(final Object tag) { //Tag不可以为空 if (tag == null) { throw new IllegalArgumentException("Cannot cancelAll with a null tag"); } cancelAll(new RequestFilter() { @Override public boolean apply(Request request) { //匹配到了 return request.getTag() == tag; } }); } /** * 添加一个请求到分发队列中 */ public Request add(Request request) { //请求保存该队列,用于标识该请求属于该队列 request.setRequestQueue(this); //同步添加请求对正在进行的请求集合中 synchronized (mCurrentRequests) { mCurrentRequests.add(request); } //给请求设置序列号 request.setSequence(getSequenceNumber()); //设置Marker标记为 request.addMarker("add-to-queue"); //如果请求不需要缓存,那添加到网络队列中就可以了 if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } // Insert request into stage if there's already a request with the same cache key in flight. synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); if (mWaitingRequests.containsKey(cacheKey)) { // There is already a request in flight. Queue up. Queue> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { // Insert 'null' queue for this cacheKey, indicating there is now a request in // flight. mWaitingRequests.put(cacheKey, null); mCacheQueue.add(request); } return request; } } /** * 结束请求 */ void finish(Request request) { //从正在进行中的请求集合中移除该请求 synchronized (mCurrentRequests) { mCurrentRequests.remove(request); } //通知回调,请求结束了 synchronized (mFinishedListeners) { for (RequestFinishedListener listener : mFinishedListeners) { listener.onRequestFinished(request); } } //如果该请求可以被缓存 if (request.shouldCache()) { synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); Queue> waitingRequests = mWaitingRequests.remove(cacheKey); if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", waitingRequests.size(), cacheKey); } // Process all queued up requests. They won't be considered as in flight, but // that's not a problem as the cache has been primed by 'request'. mCacheQueue.addAll(waitingRequests); } } } } /** * 添加一个请求结束的回调 */ public void addRequestFinishedListener(RequestFinishedListener listener) { synchronized (mFinishedListeners) { mFinishedListeners.add(listener); } } /** * 移除设置的请求结束回调 */ public void removeRequestFinishedListener(RequestFinishedListener listener) { synchronized (mFinishedListeners) { mFinishedListeners.remove(listener); } } }

CacheDispatcher

CacheDispatcher是一个缓存分发器,它继承于Thread,所以它是一个线程,那么肯定会复写run()方法

在run()方法中,主要做以下几件事

  • 设置线程优先级为后台线程
  • 缓存类初始化
  • 死循环,不断从CacheQueue缓存队列中获取Request,try-catch,如果线程被打断,则判断退出标记,是的话,则退出,不是则进入到下一次循环。
  • 判断是否被取消,被取消则进入下一次循环
  • 先尝试从缓存类中,使用请求的缓存Key,获取缓存信息,如果获取不到,则把请求加入到NetworkQueue网络队列
  • 如果缓存已过期,也加入到NetworkQueue网络队列
  • 如果缓存命中,则从缓存中获取数据和请求头,再解析它们,得到响应Response,再通过ResponseDelivery,把响应结果发到主线程,回调给我们的监听器
public class CacheDispatcher extends Thread {
    /**
     * Debug模式标记
     */
    private static final boolean DEBUG = VolleyLog.DEBUG;

    /**
     * 缓存队列,是一个BlockingQueue
     */
    private final BlockingQueue> mCacheQueue;

    /**
     * 网络队列,也是一个BlockingQueue
     */
    private final BlockingQueue> mNetworkQueue;

    /**
     * 缓存类
     */
    private final Cache mCache;

    /**
     * 网络请求结果分发器
     */
    private final ResponseDelivery mDelivery;

    /**
     * 是否退出的标记
     */
    private volatile boolean mQuit = false;

    /**
     * 构造方法,创建一个缓存分发器,必须调用 {@link #start()} 才能开始处理
     *
     * @param cacheQueue   缓存队列
     * @param networkQueue 网络队列
     * @param cache        缓存类
     * @param delivery     请求结果分发器
     */
    public CacheDispatcher(
            BlockingQueue> cacheQueue, BlockingQueue> networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * 停止缓存分发器
     */
    public void quit() {
        //设置标记
        mQuit = true;
        //中断线程
        interrupt();
    }

    @Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        //设置线程优先级
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        //缓存初始化
        mCache.initialize();

        Request request;
        //死循环,不断从队列中取出请求
        while (true) {
            //每次执行前,清除之前的请求对象,避免泄露
            request = null;
            try {
                //从缓存队列中获取一个请求
                request = mCacheQueue.take();
            } catch (InterruptedException e) {
                //被打断了,则退出
                if (mQuit) {
                    return;
                }
                continue;
            }
            try {
                request.addMarker("cache-queue-take");

                //请求被取消,结束请求即可
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                //尝试从缓存中查找缓存信息
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    //没有找到缓存,加入到网络请求队列
                    mNetworkQueue.put(request);
                    continue;
                }

                //缓存过期,也加入网络请求队列
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                //命中缓存,解析它
                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;

                    //在主线程中回调
                    final Request finalRequest = request;
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                //把请求,添加到请求队列中
                                mNetworkQueue.put(finalRequest);
                            } catch (InterruptedException e) {
                                //添加失败,不处理了
                            }
                        }
                    });
                }
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
            }
        }
    }
}

NetworkDispatcher

NetworkDispatcher和CacheDispatcher类似,也是继承于Thread,核心逻辑也是run()方法

在run()方法中,主要做以下几件事

  • 设置线程优先级为后台线程
  • 死循环,不断从网络队列中,获取Request请求,try-catch,如果线程被中断了,判断退出标志位,是则退出,否则进行下一次循环
  • 判断请求是否被取消,被取消了则结束请求
  • 通过Network,发起网络请求,获取NetworkResponse响应对象
  • 解析NetworkResponse响应对象为Response对象
  • 判断该请求是否需要被缓存,如果需要,则通过Cache操作类把请求的响应写入磁盘缓存
  • 通过ResponseDelivery,把响应结果发送到主线程,进行成功监听器回调
  • 最外层有try-catch,如果发生异常Exception,则通过ResponseDelivery回调错误监听器

Request

Request是一个抽象类,它上面有一个泛型,泛型为具体响应解析时的类型,这个基类提供了公共逻辑,不同的响应类型由子类实现,例如响应为字符串,则是StringRequest,Json请求分3个类,基类JsonRequest,JsonObject类型是JsonObjectRequest,JsonArray类型是JsonArrayRequest

  • Request

Request类实现了Comparable接口,实现compareTo()方法,目的是用于队列按优先级排序,如果优先级相等,则按照入队的顺序进行排序(先进先出)

public abstract class Request implements Comparable> {
    /**
     * 默认的参数编码
     */
    private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";

    /**
     * 支持的请求方法
     */
    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;
    }

    /**
     * An event log tracing the lifetime of this request; for debugging.
     */
    private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;

    /**
     * 当前请求的请求方法,支持 GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, and PATCH.
     */
    private final int mMethod;

    /**
     * 请求的请求地址Url
     */
    private final String mUrl;

    /**
     * 出现 3xx http响应码的重定向url
     */
    private String mRedirectUrl;

    /**
     * 请求的唯一标识
     */
    private final String mIdentifier;

    /**
     * Default tag for {@link TrafficStats}.
     */
    private final int mDefaultTrafficStatsTag;

    /**
     * 错误的回调监听器
     */
    private Response.ErrorListener mErrorListener;

    /**
     * 请求的序列号,用于请求排序,先进先出
     */
    private Integer mSequence;

    /**
     * 请求绑定请求队列
     */
    private RequestQueue mRequestQueue;

    /**
     * 是否需要缓存,默认开启
     */
    private boolean mShouldCache = true;

    /**
     * 请求是否被取消的标志位
     */
    private boolean mCanceled = false;

    /**
     * 请求是否已被响应
     */
    private boolean mResponseDelivered = false;

    /**
     * 重试策略
     */
    private RetryPolicy mRetryPolicy;

    /**
     * 缓存信息对象
     */
    private Cache.Entry mCacheEntry = null;

    /**
     * 请求的Tag,取消请求时使用
     */
    private Object mTag;

    /**
     * Creates a new request with the given URL and error listener.  Note that
     * the normal response listener is not provided here as delivery of responses
     * is provided by subclasses, who have a better idea of how to deliver an
     * already-parsed response.
     *
     * @deprecated Use {@link #Request(int, String, Response.ErrorListener)}.
     */
    @Deprecated
    public Request(String url, Response.ErrorListener listener) {
        this(Method.DEPRECATED_GET_OR_POST, url, listener);
    }

    /**
     * 构造方法,创建一个Request对象
     *
     * @param method   请求方法
     * @param url      请求Url
     * @param listener 错误回调监听器
     */
    public Request(int method, String url, Response.ErrorListener listener) {
        mMethod = method;
        mUrl = url;
        //创建请求的唯一标识
        mIdentifier = createIdentifier(method, url);
        mErrorListener = listener;
        //设置默认的重试策略
        setRetryPolicy(new DefaultRetryPolicy());

        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
    }

    /**
     * Return the method for this request.  Can be one of the values in {@link Method}.
     */
    public int getMethod() {
        return mMethod;
    }

    /**
     * Set a tag on this request. Can be used to cancel all requests with this
     * tag by {@link RequestQueue#cancelAll(Object)}.
     *
     * @return This Request object to allow for chaining.
     */
    public Request setTag(Object tag) {
        mTag = tag;
        return this;
    }

    /**
     * Returns this request's tag.
     *
     * @see Request#setTag(Object)
     */
    public Object getTag() {
        return mTag;
    }

    /**
     * @return this request's {@link Response.ErrorListener}.
     */
    public Response.ErrorListener getErrorListener() {
        return mErrorListener;
    }

    /**
     * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}
     */
    public int getTrafficStatsTag() {
        return mDefaultTrafficStatsTag;
    }

    /**
     * 返回Url的host(主机地址)的hashcode,没有的话,返回0
     */
    private static int findDefaultTrafficStatsTag(String url) {
        if (!TextUtils.isEmpty(url)) {
            Uri uri = Uri.parse(url);
            if (uri != null) {
                String host = uri.getHost();
                if (host != null) {
                    return host.hashCode();
                }
            }
        }
        return 0;
    }

    /**
     * Sets the retry policy for this request.
     *
     * @return This Request object to allow for chaining.
     */
    public Request setRetryPolicy(RetryPolicy retryPolicy) {
        mRetryPolicy = retryPolicy;
        return this;
    }

    /**
     * Adds an event to this request's event log; for debugging.
     */
    public void addMarker(String tag) {
        if (MarkerLog.ENABLED) {
            mEventLog.add(tag, Thread.currentThread().getId());
        }
    }

    /**
     * 通知RequestQueue队列,该请求已经结束
     */
    void finish(final String tag) {
        if (mRequestQueue != null) {
            //通知队列,移除掉该请求
            mRequestQueue.finish(this);
            onFinish();
        }
        //打印日志
        if (MarkerLog.ENABLED) {
            final long threadId = Thread.currentThread().getId();
            //判断当前线程如果是不是主线程
            if (Looper.myLooper() != Looper.getMainLooper()) {
                //不是主线程,通过Handler,保证Log打印是有序的
                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());
        }
    }

    /**
     * 结束时,清除监听器
     */
    protected void onFinish() {
        mErrorListener = null;
    }

    /**
     * Associates this request with the given queue. The request queue will be notified when this
     * request has finished.
     *
     * @return This Request object to allow for chaining.
     */
    public Request setRequestQueue(RequestQueue requestQueue) {
        mRequestQueue = requestQueue;
        return this;
    }

    /**
     * Sets the sequence number of this request.  Used by {@link RequestQueue}.
     *
     * @return This Request object to allow for chaining.
     */
    public final Request setSequence(int sequence) {
        mSequence = sequence;
        return this;
    }

    /**
     * Returns the sequence number of this request.
     */
    public final int getSequence() {
        if (mSequence == null) {
            throw new IllegalStateException("getSequence called before setSequence");
        }
        return mSequence;
    }

    /**
     * Returns the URL of this request.
     */
    public String getUrl() {
        return (mRedirectUrl != null) ? mRedirectUrl : mUrl;
    }

    /**
     * Returns the URL of the request before any redirects have occurred.
     */
    public String getOriginUrl() {
        return mUrl;
    }

    /**
     * Returns the identifier of the request.
     */
    public String getIdentifier() {
        return mIdentifier;
    }

    /**
     * Sets the redirect url to handle 3xx http responses.
     */
    public void setRedirectUrl(String redirectUrl) {
        mRedirectUrl = redirectUrl;
    }

    /**
     * 返回这个请求的缓存Key,默认由请求方法和请求Url组合
     */
    public String getCacheKey() {
        return mMethod + ":" + mUrl;
    }

    /**
     * 设置缓存信息对象
     */
    public Request setCacheEntry(Cache.Entry entry) {
        mCacheEntry = entry;
        return this;
    }

    /**
     * 返回缓存信息对象,如果没有则为null
     */
    public Cache.Entry getCacheEntry() {
        return mCacheEntry;
    }

    /**
     * 标记该请求已被取消,如果被取消,则不会进行回调
     */
    public void cancel() {
        mCanceled = true;
    }

    /**
     * 返回该请求是否被取消了
     */
    public boolean isCanceled() {
        return mCanceled;
    }

    /**
     * 返回配置的请求头
     *
     * @throws AuthFailureError In the event of auth failure
     */
    public Map getHeaders() throws AuthFailureError {
        return Collections.emptyMap();
    }

    /**
     * Returns a Map of POST parameters to be used for this request, or null if
     * a simple GET should be used.  Can throw {@link AuthFailureError} as
     * authentication may be required to provide these values.
     *
     * 

Note that only one of getPostParams() and getPostBody() can return a non-null * value.

* * @throws AuthFailureError In the event of auth failure * @deprecated Use {@link #getParams()} instead. */ @Deprecated protected Map getPostParams() throws AuthFailureError { return getParams(); } /** * Returns which encoding should be used when converting POST parameters returned by * {@link #getPostParams()} into a raw POST body. * *

This controls both encodings: *

    *
  1. The string encoding used when converting parameter names and values into bytes prior * to URL encoding them.
  2. *
  3. The string encoding used when converting the URL encoded parameters into a raw * byte array.
  4. *
* * @deprecated Use {@link #getParamsEncoding()} instead. */ @Deprecated protected String getPostParamsEncoding() { return getParamsEncoding(); } /** * @deprecated Use {@link #getBodyContentType()} instead. */ @Deprecated public String getPostBodyContentType() { return getBodyContentType(); } /** * 返回Body * * @throws AuthFailureError In the event of auth failure * @deprecated Use {@link #getBody()} instead. */ @Deprecated public byte[] getPostBody() throws AuthFailureError { // Note: For compatibility with legacy clients of volley, this implementation must remain // here instead of simply calling the getBody() function because this function must // call getPostParams() and getPostParamsEncoding() since legacy clients would have // overridden these two member functions for POST requests. Map postParams = getPostParams(); if (postParams != null && postParams.size() > 0) { return encodeParameters(postParams, getPostParamsEncoding()); } return null; } /** * 返回POST和PUT时的请求参数,子类可复写该方法设置自定义参数 */ protected Map getParams() throws AuthFailureError { return null; } /** * 返回在POST和PUT时,参数转换使用的编码 * *

This controls both encodings: *

    *
  1. The string encoding used when converting parameter names and values into bytes prior * to URL encoding them.
  2. *
  3. The string encoding used when converting the URL encoded parameters into a raw * byte array.
  4. *
*/ protected String getParamsEncoding() { return DEFAULT_PARAMS_ENCODING; } /** * 返回请求的Content-Type */ public String getBodyContentType() { return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); } /** * 返回请求体,请求方式为:POST和PUT时 * *

By default, the body consists of the request parameters in * application/x-www-form-urlencoded format. When overriding this method, consider overriding * {@link #getBodyContentType()} as well to match the new body format. * * @throws AuthFailureError in the event of auth failure */ public byte[] getBody() throws AuthFailureError { Map params = getParams(); if (params != null && params.size() > 0) { return encodeParameters(params, getParamsEncoding()); } return null; } /** * 将请求参数Map,转换为指定编码的字符串,例如:https://www.baidu.com/?a=xxx&b=yyy *

* Converts params into an application/x-www-form-urlencoded encoded string. * * @param params 参数Map * @param paramsEncoding 编码 */ private byte[] encodeParameters(Map params, String paramsEncoding) { StringBuilder encodedParams = new StringBuilder(); try { for (Map.Entry entry : params.entrySet()) { encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding)); encodedParams.append('='); encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding)); encodedParams.append('&'); } return encodedParams.toString().getBytes(paramsEncoding); } catch (UnsupportedEncodingException uee) { throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee); } } /** * 设置该请求是否可以被缓存 * * @param shouldCache 是否可以被缓存 */ public final Request setShouldCache(boolean shouldCache) { mShouldCache = shouldCache; return this; } /** * 该请求是否应该被缓存 */ public final boolean shouldCache() { return mShouldCache; } /** * 优先级,请求顺序会按照优先级进行处理,高优先级会优先于低优先级执行,默认按照先进先出的规则 */ public enum Priority { LOW, NORMAL, HIGH, IMMEDIATE } /** * 返回请求的优先级 {@link Priority},默认优先级为 {@link Priority#NORMAL} */ public Priority getPriority() { return Priority.NORMAL; } /** * 获取请求的超时时间 */ public final int getTimeoutMs() { return mRetryPolicy.getCurrentTimeout(); } /** * 返回该请求的重试策略 */ public RetryPolicy getRetryPolicy() { return mRetryPolicy; } /** * 标记该请求已被响应过了 */ public void markDelivered() { mResponseDelivered = true; } /** * 返回该请求是否被响应过了 */ public boolean hasHadResponseDelivered() { return mResponseDelivered; } /** * 抽象方法,子类进行实现,这个方法在子线程中调用, * * @param response 网络响应 * @return 解析后的响应,如果出错,则返回null */ abstract protected Response parseNetworkResponse(NetworkResponse response); /** * 解析请求错误,子类可以复写该方法,返回更加具体的错误类型,默认类型是VolleyError */ protected VolleyError parseNetworkError(VolleyError volleyError) { return volleyError; } /** * 抽象方法,子类必须实现,该方法用于通知监听器获取响应结果 * * @param response 响应 */ abstract protected void deliverResponse(T response); /** * 请求失败时,回调错误监听器 * * @param error 错误信息 */ public void deliverError(VolleyError error) { if (mErrorListener != null) { mErrorListener.onErrorResponse(error); } } /** * 比较方法 *

* 优先按照优先级进行排序,其次再按照序列号进行先进先出排序 */ @Override public int compareTo(Request other) { //获取要比较的2个请求的请求优先级 Priority left = this.getPriority(); Priority right = other.getPriority(); //默认优先级为NORMAL //如果优先级相等,则比较请求加入的顺序排序,先进先出 return left == right ? this.mSequence - other.mSequence : right.ordinal() - left.ordinal(); } @Override public String toString() { String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag()); return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " " + getPriority() + " " + mSequence; } /** * 唯一标识的生成次数 */ private static long sCounter; /** * 创建请求的唯一标识,它是通过请求方法和请求Url组合成一个字符串后,进行sha1Hash算法计算得出 *

* sha1(Request:method:url:timestamp:counter) * * @param method 请求方法 * @param url 请求Url * @return sha1 hash string */ private static String createIdentifier(final int method, final String url) { return InternalUtils.sha1Hash("Request:" + method + ":" + url + ":" + System.currentTimeMillis() + ":" + (sCounter++)); } }

  • StringRequest
public class StringRequest extends Request {
    private Listener mListener;

    /**
     * 创建一个StringRequest,可以指定请求方法
     *
     * @param method        请求方法 {@link Method}
     * @param url           URL
     * @param listener      响应监听器
     * @param errorListener 错误监听器
     */
    public StringRequest(int method, String url, Listener listener,
                         ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    /**
     * 创建一个StringRequest,使用GET请求
     *
     * @param url           URL
     * @param listener      响应监听器
     * @param errorListener 错误监听器
     */
    public StringRequest(String url, Listener listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    protected void onFinish() {
        super.onFinish();
        //请求完成时,清理回调
        mListener = null;
    }

    @Override
    protected void deliverResponse(String response) {
        //回调响应
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }

    @Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        //解析响应
        String parsed;
        try {
            //指定字符串级生成字符串
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            //生成失败,直接用原始数据
            parsed = new String(response.data);
        }
        //返回响应
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}
  • JsonRequest

JSON请求基类

public abstract class JsonRequest extends Request {
    /**
     * 默认字符集
     */
    protected static final String PROTOCOL_CHARSET = "utf-8";

    /**
     * JSON请求的Content-Type
     */
    private static final String PROTOCOL_CONTENT_TYPE =
            String.format("application/json; charset=%s", PROTOCOL_CHARSET);

    /**
     * 响应监听器
     */
    private Listener mListener;
    /**
     * JSON体
     */
    private final String mRequestBody;

    /**
     * 废弃构造方法,默认GET请求,如果 {@link #getPostBody()} 或 {@link #getPostParams()} 被复写,则为POST请求
     */
    public JsonRequest(String url, String requestBody, Listener listener,
                       ErrorListener errorListener) {
        this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
    }

    /**
     * 手动指定请求方法,进行JSON请求
     */
    public JsonRequest(int method, String url, String requestBody, Listener listener,
                       ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
        mRequestBody = requestBody;
    }

    @Override
    protected void onFinish() {
        super.onFinish();
        //请求结束,清理回调
        mListener = null;
    }

    @Override
    protected void deliverResponse(T response) {
        //回调响应成功
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }

    /**
     * 子类进行JsonObject或JsonArray的处理
     *
     * @param response 响应实体
     */
    @Override
    abstract protected Response parseNetworkResponse(NetworkResponse response);

    /**
     * @deprecated Use {@link #getBodyContentType()}.
     */
    @Override
    public String getPostBodyContentType() {
        return getBodyContentType();
    }

    /**
     * @deprecated Use {@link #getBody()}.
     */
    @Override
    public byte[] getPostBody() {
        return getBody();
    }

    @Override
    public String getBodyContentType() {
        return PROTOCOL_CONTENT_TYPE;
    }

    @Override
    public byte[] getBody() {
        //JSON字符串转换为byte数组
        try {
            return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
        } catch (UnsupportedEncodingException uee) {
            VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
                    mRequestBody, PROTOCOL_CHARSET);
            return null;
        }
    }
}
  • JsonObjectRequest

JsonObject类型的请求,主要在parseNetworkResponse()方法,把字符串Json解析为JSONObject类型

public class JsonObjectRequest extends JsonRequest {
    //省略构造方法

    @Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        try {
            //解析响应为字符串
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
            //再把字符串解析为JSONObject返回
            return Response.success(new JSONObject(jsonString),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException | JSONException e) {
            return Response.error(new ParseError(e));
        }
    }
}
  • JsonArrayRequest
public class JsonArrayRequest extends JsonRequest {
    //省略构造方法
    @Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        try {
            //解析响应为字符串
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
            //再把字符串解析为JSONArray返回
            return Response.success(new JSONArray(jsonString),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException | JSONException e) {
            return Response.error(new ParseError(e));
        }
    }
}

Network

Network接口,是一个网络请求接口,只有一个performRequest()方法,目的就是执行请求,他只有一个实现类BasicNetwork,内部通过HttpStack的进行真正的接口请求,那为什么不直接使用HttpStack还要套一层呢?

因为,Network还负责响应的转换,异常状态码的处理,请求的重定向,以及请求失败后的重试逻辑(委托给RetryPolicy,该类是重试策略类,只有一个唯一子类DefaultRetryPolicy,使用方也可以拓展子类进行不同策略)

performRequest()方法中,重试的实现,使用死循环,如果出错会进行重试,如果重试次数到了,则会抛出异常,中断这个死循环

public class BasicNetwork implements Network {
    protected static final boolean DEBUG = VolleyLog.DEBUG;

    /**
     * 判断是否是慢请求的超时时间
     */
    private static final int SLOW_REQUEST_THRESHOLD_MS = 3000;

    private static final int DEFAULT_POOL_SIZE = 4096;

    /**
     * HttpStack实现类
     */
    protected final HttpStack mHttpStack;

    /**
     * 缓冲池
     */
    protected final ByteArrayPool mPool;

    /**
     * 构造方法,可以指定HttpStack实现,使用默认的缓冲池
     */
    public BasicNetwork(HttpStack httpStack) {
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    }

    /**
     * 构造方法,可以指定HttpStack实现和缓冲池
     *
     * @param httpStack HttpStack实现
     * @param pool      缓冲池
     */
    public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
        mHttpStack = httpStack;
        mPool = pool;
    }

    @Override
    public NetworkResponse performRequest(Request request) throws VolleyError {
        //开始时间
        long requestStart = SystemClock.elapsedRealtime();
        //死循环,如果成功则跳出循环,出错会重试,重试到一定次数后,抛出异常,跳出循环
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map responseHeaders = Collections.emptyMap();
            try {
                //创建请求头
                Map headers = new HashMap();
                //添加缓存请求头
                addCacheHeaders(headers, request.getCacheEntry());
                //发起请求
                httpResponse = mHttpStack.performRequest(request, headers);
                //获取响应状态行
                StatusLine statusLine = httpResponse.getStatusLine();
                //获取响应状态码
                int statusCode = statusLine.getStatusCode();

                //转换响应头
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                //处理缓存和验证
                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
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                //301和302状态码,是重定向,301代表请求的资源永久被移除了,而302是资源还在,但临时地跳转到另外一个链接
                if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    String newUrl = responseHeaders.get("Location");
                    request.setRedirectUrl(newUrl);
                }

                //处理内容
                if (httpResponse.getEntity() != null) {
                    responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                    //处理没有内容的响应
                    responseContents = new byte[0];
                }

                //打印慢请求
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                //200和299的状态码处理
                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 = 0;
                NetworkResponse networkResponse = null;
                if (httpResponse != null) {
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                } else {
                    throw new NoConnectionError(e);
                }
                //301、302重定向
                if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
                        statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());
                } else {
                    VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                }
                if (responseContents != null) {
                    networkResponse = new NetworkResponse(statusCode, responseContents,
                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                    //401和403,进行重试
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                    } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
                            statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                        //301、302,重试到新地铁
                        attemptRetryOnException("redirect",
                                request, new RedirectError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        //其他则是服务器异常
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(e);
                }
            }
        }
    }

    /**
     * 慢请求打印
     */
    private void logSlowRequests(long requestLifetime, Request request,
                                 byte[] responseContents, StatusLine statusLine) {
        if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
            VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
                            "[rc=%d], [retryCount=%s]", request, requestLifetime,
                    responseContents != null ? responseContents.length : "null",
                    statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
        }
    }

    /**
     * 重试请求,如果达到重试次数会抛出异常
     *
     * @param logPrefix Log打印的前缀
     * @param request   请求对象
     * @param exception 到达重试次数后,会抛出的异常
     */
    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));
    }

    /**
     * 添加Http缓存头
     */
    private void addCacheHeaders(Map headers, Entry entry) {
        //该请求不需要缓存,不需要添加
        if (entry == null) {
            return;
        }

        if (entry.etag != null) {
            headers.put("If-None-Match", entry.etag);
        }

        if (entry.lastModified > 0) {
            Date refTime = new Date(entry.lastModified);
            headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
        }
    }

    /**
     * 打印错误
     */
    protected void logError(String what, String url, long start) {
        long now = SystemClock.elapsedRealtime();
        VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
    }

    /**
     * 把HttpEntity的内容转换到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();
        }
    }

    /**
     * 把 Headers[] 转换到 Map
     */
    protected static Map convertHeaders(Header[] headers) {
        Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        for (int i = 0; i < headers.length; i++) {
            result.put(headers[i].getName(), headers[i].getValue());
        }
        return result;
    }
}

RetryPolicy

RetryPolicy类是重试策略接口,有一个默认实例DefaultRetryPolicy,里面定义了重试次数和超时时间,以及一个重试间隔时间的间隔因子(每次重试,重试的间隔时间会成倍数增加)

主要是retry()方法,该方法会统计累计的重试次数,如果到达了最大重试次数,则抛出retry()方法传入的异常

  • RetryPolicy
public interface RetryPolicy {
    /**
     * 返回当前超时时间(用于日志记录)
     */
    int getCurrentTimeout();

    /**
     * 返回当前重试次数(用于日志记录)
     */
    int getCurrentRetryCount();

    /**
     * 重试
     */
    void retry(VolleyError error) throws VolleyError;
}
  • DefaultRetryPolicy
public class DefaultRetryPolicy implements RetryPolicy {
    /**
     * 超时时间
     */
    private int mCurrentTimeoutMs;

    /**
     * 已重试的次数
     */
    private int mCurrentRetryCount;

    /**
     * 最大重试次数
     */
    private final int mMaxNumRetries;

    /**
     * 失败后,重连的间隔因子
     */
    private final float mBackoffMultiplier;

    /**
     * 默认超时时间
     */
    public static final int DEFAULT_TIMEOUT_MS = 2500;

    /**
     * 默认重试次数
     */
    public static final int DEFAULT_MAX_RETRIES = 0;

    /**
     * 默认失败之后重连的间隔因子为1
     */
    public static final float DEFAULT_BACKOFF_MULT = 1f;


    /**
     * 使用默认参数,进行构造
     */
    public DefaultRetryPolicy() {
        this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
    }

    /**
     * 构造方法
     *
     * @param initialTimeoutMs  超时时间
     * @param maxNumRetries     最大重试次数
     * @param backoffMultiplier 重试间隔
     */
    public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
        mCurrentTimeoutMs = initialTimeoutMs;
        mMaxNumRetries = maxNumRetries;
        mBackoffMultiplier = backoffMultiplier;
    }

    /**
     * 获取超时时间
     */
    @Override
    public int getCurrentTimeout() {
        return mCurrentTimeoutMs;
    }

    /**
     * 获取,已重试的次数
     */
    @Override
    public int getCurrentRetryCount() {
        return mCurrentRetryCount;
    }

    /**
     * 获取,失败后,重连的间隔因子
     */
    public float getBackoffMultiplier() {
        return mBackoffMultiplier;
    }

    /**
     * 重试
     */
    @Override
    public void retry(VolleyError error) throws VolleyError {
        //重试1次,记录数量
        mCurrentRetryCount++;
        //计算重试时间
        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
        if (!hasAttemptRemaining()) {
            //到达重试最大次数,还是失败了,抛异常
            throw error;
        }
    }

    /**
     * 判断是否还可以重试,还可以重试返回true,不可以重试返回false
     */
    protected boolean hasAttemptRemaining() {
        return mCurrentRetryCount <= mMaxNumRetries;
    }
}

HttpStack

HttpStack是请求接口,有2个实现类,HurlStack,它是HttpURLConnection实现,HttpClientStack,它是HttpClient实现

  • HttpStack

只有一个performRequest()方法,这个方法就是处理具体的Request请求,以及附带的请求头

public interface HttpStack {
    /**
     * 使用指定参数,执行Http请求
     *
     * request.getPostBody() == null,则发送GET请求,不为null则发送POST请求
     * 并且,Content-Type请求头的取值从 request.getPostBodyContentType() 中获取
     *
     * @param request           请求对象
     * @param additionalHeaders 附带的请求头
     * @return HTTP的响应
     */
    HttpResponse performRequest(Request request, Map additionalHeaders)
            throws IOException, AuthFailureError;
}
  • HurlStack

内部使用HttpURLConnection发出请求,而响应实体HttpResponse,因为要统一返回值类型,统一都是HttpClient的类,所以会有转换的过程

public class HurlStack implements HttpStack {
    /**
     * Content-Type请求头标记
     */
    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    /**
     * URL转换接口
     */
    public interface UrlRewriter {
        /**
         * 在请求前,可以重写URL
         *
         * @param originalUrl 原始URL
         * @return 返回重写后的URL,返回null则代表这个请求不应该被请求,会抛出异常
         */
        String rewriteUrl(String originalUrl);
    }

    private final UrlRewriter mUrlRewriter;
    private final SSLSocketFactory mSslSocketFactory;

    public HurlStack() {
        this(null);
    }

    /**
     * @param urlRewriter 重写URL
     */
    public HurlStack(UrlRewriter urlRewriter) {
        this(urlRewriter, null);
    }

    /**
     * @param urlRewriter      重写URL
     * @param sslSocketFactory 用于HTTPS连接的SSL工厂
     */
    public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
        mUrlRewriter = urlRewriter;
        mSslSocketFactory = sslSocketFactory;
    }

    @Override
    public HttpResponse performRequest(Request request, Map additionalHeaders)
            throws IOException, AuthFailureError {
        //获取请求Url
        String url = request.getUrl();
        //获取请求头
        HashMap map = new HashMap();
        map.putAll(request.getHeaders());
        map.putAll(additionalHeaders);
        //请求前,可以重写URL
        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);
        //打开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,要抛出异常给调用方
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        }
        //设置响应码和响应信息到响应对象中
        StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                connection.getResponseCode(), connection.getResponseMessage());
        BasicHttpResponse response = new BasicHttpResponse(responseStatus);
        //有响应体则把响应体转换为HttpEntity设置到响应中
        if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
            response.setEntity(entityFromConnection(connection));
        }
        //设置头
        for (Entry> header : connection.getHeaderFields().entrySet()) {
            if (header.getKey() != null) {
                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                response.addHeader(h);
            }
        }
        //返回响应信息
        return response;
    }

    /**
     * 检查响应是否有响应体
     *
     * @param requestMethod 请求方法
     * @param responseCode  响应状态码
     */
    private static boolean hasResponseBody(int requestMethod, int responseCode) {
        return requestMethod != Method.HEAD
                && !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK)
                && responseCode != HttpStatus.SC_NO_CONTENT
                && responseCode != HttpStatus.SC_NOT_MODIFIED;
    }

    /**
     * 把指定的HttpURLConnection转换为HttpEntity
     */
    private static HttpEntity entityFromConnection(HttpURLConnection connection) {
        BasicHttpEntity entity = new BasicHttpEntity();
        InputStream inputStream;
        try {
            inputStream = connection.getInputStream();
        } catch (IOException ioe) {
            inputStream = connection.getErrorStream();
        }
        entity.setContent(inputStream);
        entity.setContentLength(connection.getContentLength());
        entity.setContentEncoding(connection.getContentEncoding());
        entity.setContentType(connection.getContentType());
        return entity;
    }

    /**
     * 以指定的URL,创建一个HttpURLConnection连接
     */
    protected HttpURLConnection createConnection(URL url) throws IOException {
        return (HttpURLConnection) url.openConnection();
    }

    /**
     * 打开一个带参数的HttpURLConnection
     *
     * @param url URL地址
     * @return 链接
     * @throws IOException 异常
     */
    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);

        //如果是https链接,设置调用时配置的SSL工厂
        if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
            ((HttpsURLConnection) connection).setSSLSocketFactory(mSslSocketFactory);
        }

        return connection;
    }

    /**
     * 设置请求参数和请求方法
     */
    @SuppressWarnings("deprecation")
    /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
                                                                Request request) throws IOException, AuthFailureError {
        //设置请求方法
        switch (request.getMethod()) {
            case Method.DEPRECATED_GET_OR_POST:
                //一种不建议使用的方式,如果Body不为空,则设置为POST请求
                byte[] postBody = request.getPostBody();
                if (postBody != null) {
                    // Prepare output. There is no need to set Content-Length explicitly,
                    // since this is handled by HttpURLConnection using the size of the prepared
                    // output stream.
                    connection.setDoOutput(true);
                    connection.setRequestMethod("POST");
                    //设置Content-Type
                    connection.addRequestProperty(HEADER_CONTENT_TYPE,
                            request.getPostBodyContentType());
                    //设置请求体
                    DataOutputStream out = new DataOutputStream(connection.getOutputStream());
                    out.write(postBody);
                    out.close();
                }
                break;
            case Method.GET:
                //默认不设置请求头的话,HttpURLConnection的请求方法为GET,这里再显式设置一下
                connection.setRequestMethod("GET");
                break;
            case Method.DELETE:
                connection.setRequestMethod("DELETE");
                break;
            case Method.POST:
                connection.setRequestMethod("POST");
                addBodyIfExists(connection, request);
                break;
            case Method.PUT:
                connection.setRequestMethod("PUT");
                addBodyIfExists(connection, request);
                break;
            case Method.HEAD:
                connection.setRequestMethod("HEAD");
                break;
            case Method.OPTIONS:
                connection.setRequestMethod("OPTIONS");
                break;
            case Method.TRACE:
                connection.setRequestMethod("TRACE");
                break;
            case Method.PATCH:
                connection.setRequestMethod("PATCH");
                addBodyIfExists(connection, request);
                break;
            default:
                throw new IllegalStateException("Unknown method type.");
        }
    }

    /***
     * 设置请求体,如果有的话
     */
    private static void addBodyIfExists(HttpURLConnection connection, Request request)
            throws IOException, AuthFailureError {
        byte[] body = request.getBody();
        if (body != null) {
            connection.setDoOutput(true);
            connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
            DataOutputStream out = new DataOutputStream(connection.getOutputStream());
            out.write(body);
            out.close();
        }
    }
}
  • HttpClientStack
public class HttpClientStack implements HttpStack {
    /**
     * HttpClient实例
     */
    protected final HttpClient mClient;

    /**
     * Content-Type标识
     */
    private final static String HEADER_CONTENT_TYPE = "Content-Type";

    public HttpClientStack(HttpClient client) {
        mClient = client;
    }

    /**
     * 给HttpUriRequest对象设置请求头
     */
    private static void addHeaders(HttpUriRequest httpRequest, Map headers) {
        for (String key : headers.keySet()) {
            httpRequest.setHeader(key, headers.get(key));
        }
    }

    /**
     * Map请求参数Map转List
     */
    @SuppressWarnings("unused")
    private static List getPostParameterPairs(Map postParams) {
        List result = new ArrayList(postParams.size());
        for (String key : postParams.keySet()) {
            result.add(new BasicNameValuePair(key, postParams.get(key)));
        }
        return result;
    }

    @Override
    public HttpResponse performRequest(Request request, Map 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();
        //设置超时时间
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
        //执行请求
        return mClient.execute(httpRequest);
    }

    /**
     * 为请求创建对应的HttpUriRequest子类
     */
    @SuppressWarnings("deprecation")
    /* protected */ static HttpUriRequest createHttpRequest(Request request,
                                                            Map 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.
                //有请求体,则设置为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 {
                    //没有请求体,则设置给GET请求
                    return new HttpGet(request.getUrl());
                }
            }
            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());
                //设置Content-Type
                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;
            }
            case Method.HEAD:
                return new HttpHead(request.getUrl());
            case Method.OPTIONS:
                return new HttpOptions(request.getUrl());
            case Method.TRACE:
                return new HttpTrace(request.getUrl());
            case Method.PATCH: {
                HttpPatch patchRequest = new HttpPatch(request.getUrl());
                patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                setEntityIfNonEmptyBody(patchRequest, request);
                return patchRequest;
            }
            default:
                throw new IllegalStateException("Unknown request method.");
        }
    }

    /**
     * 设置请求体,如果不为空
     */
    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);
        }
    }

    /**
     * 在执行请求前回调,子类可以复写该方法做额外处理
     */
    protected void onPrepareRequest(HttpUriRequest request) throws IOException {
        // Nothing.
    }

    /**
     * PATCH类型请求,这个请求方式在安卓中不存在,所以在这里定义
     */
    public static final class HttpPatch extends HttpEntityEnclosingRequestBase {
        public final static String METHOD_NAME = "PATCH";

        public HttpPatch() {
            super();
        }

        public HttpPatch(final URI uri) {
            super();
            setURI(uri);
        }

        /**
         * @throws IllegalArgumentException if the uri is invalid.
         */
        public HttpPatch(final String uri) {
            super();
            setURI(URI.create(uri));
        }

        @Override
        public String getMethod() {
            return METHOD_NAME;
        }
    }
}

ResponseDelivery

ResponseDelivery是响应分发器接口,主要是把请求响应或请求错误信息,发送到主线程中,回调我们的响应监听器和错误监听器,至于线程切换自然是使用Handler

ResponseDelivery接口,有一个子类ExecutorDelivery

  • ResponseDelivery
public interface ResponseDelivery {
    /**
     * 传递响应
     */
    void postResponse(Request request, Response response);

    /**
     * 传递响应,并且附带的Runnable将在传递之后执行
     */
    void postResponse(Request request, Response response, Runnable runnable);

    /**
     * 传递一个错误
     */
    void postError(Request request, VolleyError error);
}
  • ExecutorDelivery

发现会把Handler封装成一个Executor,这个类是线程池中的类,只有一个execute()方法,执行传入的Runnable

public class ExecutorDelivery implements ResponseDelivery {
    /**
     * 传递响应的线程切换Executor,一般为主线程
     */
    private final Executor mResponsePoster;

    /**
     * 构造方法,传入Handler
     *
     * @param handler 通过这个 {@link Handler} 进行回调
     */
    public ExecutorDelivery(final Handler handler) {
        //创建Executor,它会使用Handler进行执行
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }

    /**
     * 构造方法,传入指定的Executor进行执行和回调
     */
    public ExecutorDelivery(Executor executor) {
        mResponsePoster = executor;
    }

    @Override
    public void postResponse(Request request, Response response) {
        postResponse(request, response, null);
    }

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

    @Override
    public void postError(Request request, VolleyError error) {
        request.addMarker("post-error");
        Response response = Response.error(error);
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
    }

    /**
     * 这个Runnable用于包装,用于在主线程回调
     */
    @SuppressWarnings("rawtypes")
    private static 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 (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            //响应成功或失败,这里会回调调用方的监听器
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            //如果是一个中间响应,标记一下
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                //不是中间响应,标记完成
                mRequest.finish("done");
            }

            //执行额外的Runnable
            if (mRunnable != null) {
                mRunnable.run();
            }
        }
    }
}

分析到这里,整个请求链路基本清晰了,剩下Cache缓存接口还要分析下

Cache

Cache是缓存操作接口,有2个子类,DiskBasedCache是磁盘缓存类,NoCache则是不进行缓存的类,是对接口的所有方法进行空实现

  • Cache
public interface Cache {
    /**
     * 查询缓存
     *
     * @param key 缓存Ke
     * @return An {@link Entry} or null in the event of a cache miss
     */
    Entry get(String key);

    /**
     * 添加或更新缓存
     *
     * @param key   缓存Key
     * @param entry Data to store and metadata for cache coherency, TTL, etc.
     */
    void put(String key, Entry entry);

    /**
     * 缓存初始化,该方法在子线程中回调
     */
    void initialize();

    /**
     * 让一个缓存失效
     *
     * @param key        缓存Key
     * @param fullExpire true为完全过期,false为软过期
     */
    void invalidate(String key, boolean fullExpire);

    /**
     * 移除一个缓存
     *
     * @param key 缓存Key
     */
    void remove(String key);

    /**
     * 清空缓存
     */
    void clear();

    /**
     * 缓存实体,保存请求数据
     */
    class Entry {
        /**
         * 缓存的数据
         */
        public byte[] data;

        /**
         * ETag 保证缓存一致性
         */
        public String etag;

        /**
         * 服务端返回数据的时间
         */
        public long serverDate;

        /**
         * 缓存对象的最后修改日期
         */
        public long lastModified;

        /**
         * 记录TTL
         */
        public long ttl;

        /**
         * 记录 Soft TTL
         */
        public long softTtl;

        /**
         * 服务端返回的不可变的响应头
         */
        public Map responseHeaders = Collections.emptyMap();

        /**
         * 返回true,则代表过期
         */
        public boolean isExpired() {
            return this.ttl < System.currentTimeMillis();
        }

        /**
         * 是否需要刷新数据
         */
        public boolean refreshNeeded() {
            return this.softTtl < System.currentTimeMillis();
        }
    }
}
  • DiskBasedCache
/**
 * 缓存实现,缓存到磁盘上指定目录,默认缓存大小为5M,但可以配置
 */
public class DiskBasedCache implements Cache {
    /**
     * LinkedHashMap实现的LRU算法,按照使用的顺序进行排序
     */
    private final Map mEntries = new LinkedHashMap<>(16, .75f, true);

    /**
     * 当前缓存的总大小,以字节为单位
     */
    private long mTotalSize = 0;

    /**
     * 缓存的根目录
     */
    private final File mRootDirectory;

    /**
     * 最大缓存容量
     */
    private final int mMaxCacheSizeInBytes;

    /**
     * 默认的最大缓存容量,默认5M
     */
    private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;

    /**
     * 缓存的负载因子,到达这个点后,会进行缓存清理
     */
    private static final float HYSTERESIS_FACTOR = 0.9f;

    /**
     * Magic number for current version of cache file format.
     */
    private static final int CACHE_MAGIC = 0x20150306;

    /**
     * 构造方法,在指定目录下创建缓存,并指定最大缓存大小
     *
     * @param rootDirectory       缓存根目录
     * @param maxCacheSizeInBytes 最大缓存容量
     */
    public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
        mRootDirectory = rootDirectory;
        mMaxCacheSizeInBytes = maxCacheSizeInBytes;
    }

    /**
     * 构造方法,使用默认最大缓存容量进行创建,默认5M
     *
     * @param rootDirectory The root directory of the cache.
     */
    public DiskBasedCache(File rootDirectory) {
        this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
    }

    /**
     * 清除磁盘上的所有缓存
     */
    @Override
    public synchronized void clear() {
        File[] files = mRootDirectory.listFiles();
        if (files != null) {
            for (File file : files) {
                file.delete();
            }
        }
        mEntries.clear();
        mTotalSize = 0;
        VolleyLog.d("Cache cleared.");
    }

    /**
     * 查询缓存
     */
    @Override
    public synchronized Entry get(String key) {
        //通过缓存Key查询缓存
        CacheHeader entry = mEntries.get(key);
        //没有查找到,返回null
        if (entry == null) {
            return null;
        }
        //查找到了,从磁盘上获取文件对象
        File file = getFileForKey(key);
        CountingInputStream cis = null;
        try {
            cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
            CacheHeader.readHeader(cis); // eat header
            byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
            //返回数据
            return entry.toCacheEntry(data);
        } catch (IOException e) {
            VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
            remove(key);
            return null;
        } catch (NegativeArraySizeException e) {
            VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
            remove(key);
            return null;
        } finally {
            if (cis != null) {
                try {
                    cis.close();
                } catch (IOException ioe) {
                    return null;
                }
            }
        }
    }

    /**
     * 初始化缓存目录,并扫描该目录下的缓存文件到内存中
     */
    @Override
    public synchronized void initialize() {
        if (!mRootDirectory.exists()) {
            if (!mRootDirectory.mkdirs()) {
                VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
            }
            return;
        }

        File[] files = mRootDirectory.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            BufferedInputStream fis = null;
            try {
                fis = new BufferedInputStream(new FileInputStream(file));
                CacheHeader entry = CacheHeader.readHeader(fis);
                entry.size = file.length();
                putEntry(entry.key, entry);
            } catch (IOException e) {
                if (file != null) {
                    file.delete();
                }
            } finally {
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException ignored) {
                }
            }
        }
    }

    /**
     * Invalidates an entry in the cache.
     *
     * @param key        Cache key
     * @param fullExpire True to fully expire the entry, false to soft expire
     */
    @Override
    public synchronized void invalidate(String key, boolean fullExpire) {
        Entry entry = get(key);
        if (entry != null) {
            entry.softTtl = 0;
            if (fullExpire) {
                entry.ttl = 0;
            }
            put(key, entry);
        }
    }

    /**
     * 添加或更新缓存
     */
    @Override
    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());
        }
    }

    /**
     * Removes the specified key from the cache if it exists.
     */
    @Override
    public synchronized void remove(String key) {
        boolean deleted = getFileForKey(key).delete();
        removeEntry(key);
        if (!deleted) {
            VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
                    key, getFilenameForKey(key));
        }
    }

    /**
     * Creates a pseudo-unique filename for the specified cache key.
     *
     * @param key The key to generate a file name for.
     * @return A pseudo-unique filename.
     */
    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;
    }

    /**
     * 使用缓存Key从磁盘上找缓存文件,并返回
     */
    public File getFileForKey(String key) {
        return new File(mRootDirectory, getFilenameForKey(key));
    }

    /**
     * 修剪缓存容量
     *
     * @param neededSpace 准备要添加的缓存容量大小
     */
    private void pruneIfNeeded(int neededSpace) {
        //当前容量 + 准备要添加的缓存容量,如果小于最大值,则还没有满容量,不做修剪
        if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
            return;
        }
        if (VolleyLog.DEBUG) {
            VolleyLog.v("Pruning old cache entries.");
        }

        long before = mTotalSize;
        int prunedFiles = 0;
        long startTime = SystemClock.elapsedRealtime();

        Iterator> iterator = mEntries.entrySet().iterator();
        //遍历LinkedHashMap,删掉头部数据
        while (iterator.hasNext()) {
            Map.Entry entry = iterator.next();
            CacheHeader e = entry.getValue();
            //删除缓存
            boolean deleted = getFileForKey(e.key).delete();
            if (deleted) {
                //删除成功,更新缓存容量
                mTotalSize -= e.size;
            } else {
                VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
                        e.key, getFilenameForKey(e.key));
            }
            iterator.remove();
            prunedFiles++;

            //修剪完成,跳出循环
            if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
                break;
            }
        }

        if (VolleyLog.DEBUG) {
            VolleyLog.v("pruned %d files, %d bytes, %d ms",
                    prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime);
        }
    }
    

    /**
     * 添加一个缓存
     */
    private void putEntry(String key, CacheHeader entry) {
        if (!mEntries.containsKey(key)) {
            mTotalSize += entry.size;
        } else {
            CacheHeader oldEntry = mEntries.get(key);
            mTotalSize += (entry.size - oldEntry.size);
        }
        mEntries.put(key, entry);
    }

    /**
     * 移除一个缓存
     */
    private void removeEntry(String key) {
        CacheHeader entry = mEntries.get(key);
        if (entry != null) {
            mTotalSize -= entry.size;
            mEntries.remove(key);
        }
    }
    
    //省略不重要的代码..
}
  • NoCache

不缓存的实现,所有操作方法都是空实现

public class NoCache implements Cache {
    @Override
    public void clear() {
    }

    @Override
    public Entry get(String key) {
        return null;
    }

    @Override
    public void put(String key, Entry entry) {
    }

    @Override
    public void invalidate(String key, boolean fullExpire) {
    }

    @Override
    public void remove(String key) {
    }

    @Override
    public void initialize() {
    }
}

鸿蒙上使用Volley

鸿蒙上没有HttpClient的API,所以不能使用安卓上的内置HttpClient,但Github上面有一个把内置HttpClient源码拿出来单独编译的HttpClient,所以我们可以直接用它,并且它的包名都成了cz.msebera.android,改掉导包就好

以及JSONObject和JSONArray,Android上是把它们放到了android.jar,而鸿蒙中没有,所以要替换,这里我换成FastJson的JSONObject和JSONArray,修改一下JsonObjectRequestJsonArrayRequest中的Json解析即可

下面给出所需要的HttpClient和FastJson依赖

dependencies {
    //鸿蒙中没有JsonObject和JsonArray的类,Android中是在android.jar中,鸿蒙中没有,所以只能用FastJson
    api 'com.alibaba:fastjson:1.2.76'
    //因为鸿蒙没有HttpClient的API,所以单独引用
    api 'cz.msebera.android:httpclient:4.5.8'
}

总结

首先通过Volley创建RequestQueue请求队列,内部有一个网络请求队列和缓存队列

创建1个缓存分发器,4个网络请求分发器,分发器实际是一个线程,不断死循环从队列中拿取请求出来处理

发出请求使用Network接口,而实际实现是BasicNetwork,内部会通过HttpStack发出请求,它有个2个实现类,HurlStack和HttpClientStack

请求成功判断请求是否需要缓存,如果需要则使用Cache接口进行缓存,而具体实现是DiskBasedCache,会把请求的响应、请求头等缓存到磁盘中。如果请求失败,则使用RetryPolicy缓存策略进行重试

最后请求成功或请求失败,会通过ResponseDelivery进行分发,把响应结果或错误发送到主线程,回调响应监听器或错误监听器

你可能感兴趣的:(Volley 源码分析)