Architecture(2)Volley源码分析

概述

Volley是Google推出的一款比较轻巧的网络请求框架,并且可以对请求进行缓存,同时可以实时取消请求,设置请求优先级,内置了ImageRequest,JsonRequest,JsonObjectRequest,JsonArrayRequest,StringRequest等,并且还支持自定义Request,基本上能满足日常的开发,当让Volley原生并不支持文件上传,但是可以通过自定义Request来实现,Volley不仅仅支持网络请求,还支持图片加载,这个主要是通过内置的ImageRequest来实现,Volley的工作原理大致如下:

Volley_final

大致流程就是,当添加一个Request的时候,首先会被添加到CachaQueue中,

正文

工作流程

flow

Volley的缓存跟常规的缓存不太一致,它并不是直接去取缓存,而是构造了一个缓存队列,存放Request,然后根据特有的key值去取缓存,如果缓存存在并且没有过期,请求也没有取消,那么就直接解析缓存数据,发送到主线程,不然就直接加入到网络请求队列,重新请求网络数据,Volley的源码比较多,下面主要是从RequestRequestQueueDispatcher,Cache,这几个类分析一下Volley的一些实现细节,毕竟大部分框架,原理都是一两句话都能说清楚,但是有很多细节让自己实现其实还是挺困难的。

Request

继承关系

Request

Request是一个单独的类,实现了Comparable接口,主要是用来对请求进行排序,如果设置了请求的优先级,就会根据优先级来进行排序,如果没有优先级就会按照请求加入的顺序来排序。

成员变量


private final int mMethod;//请求类型,GET,POST
private final String mUrl;//请求的服务器地址
private final Object mLock = new Object();//用来给对象上锁
private Response.ErrorListener mErrorListener;//请求失败的监听
private Integer mSequence;//请求的序列号,按照请求的顺序依次递增
private RequestQueue mRequestQueue;//请求队列
private boolean mShouldCache = true;//是否需要缓存,默认开启缓存
private boolean mCanceled = false;//请求是否取消,默认为false
private boolean mResponseDelivered = false;//解析完的请求是否已经发送
private boolean mShouldRetryServerErrors = false;//遇到服务器异常是否需要重试
private RetryPolicy mRetryPolicy;//重试策略
private Cache.Entry mCacheEntry = null;//缓存的 对象,里面封装了很多跟缓存相关的信息
private Object mTag;//请求的tag
private NetworkRequestCompleteListener mRequestCompleteListener;//网络请求完成回调的结果
//Request的生命周期记录工具
private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;

Method

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

由于方法的辨识度比较高,所以Volley没有采用枚举,而是采用了接口内定义变量,节省开销

Priority

public enum Priority {
    LOW,
    NORMAL,
    HIGH,
    IMMEDIATE
}

优先级更注重可读性,所以Volley采用了枚举

构造方法

@Deprecated
public Request(String url, Response.ErrorListener listener) {
    this(Method.DEPRECATED_GET_OR_POST, url, listener);
}

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

传入method,url以及失败的ErrorListener

关键方法

cancel

取消请求

public void cancel() {
    synchronized (mLock) {
        mCanceled = true;//改变请求标志位
        mErrorListener = null;//回调接口置空
    }
}

compareTo

设置请求优先级

@Override
public int compareTo(Request<T> other) {
   //获取请求优先级
    Priority left = this.getPriority();
    Priority right = other.getPriority();
    //请求优先级默认为Normal
    //1.先比较请求优先级,如果相等再比较请求加入的顺序
    return left == right ?
            this.mSequence - other.mSequence :
            right.ordinal() - left.ordinal();
}

finish

通知RequestQueue,这个请求已经结束

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()) {
          //通过PostRunnable的方式,保证请求结束的打印时间是有序的
            Handler mainThread = new Handler(Looper.getMainLooper());
            mainThread.post(new Runnable() {
                @Override
                public void run() {
                    mEventLog.add(tag, threadId);
                    mEventLog.finish(Request.this.toString());
                }
            });
            return;
        }
        mEventLog.add(tag, threadId);
        mEventLog.finish(this.toString());
    }
}

getCacheKey

默认作为请求的服务器地址作为key,实际开发过程中需要通过MD5会比较好一点

public String getCacheKey() {
    return getUrl();
}

抽象方法

Rquest是一个抽象类,里面还有很多抽象犯法需要子类去实现

//解析网络请求
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
//传递网络请求结果
abstract protected void deliverResponse(T response);
//请求失败的回调,因为不同的Request需要的返回类型不一样,需要子类实现,但是请求失败确是共同的
//所以Volley做了一些封装
public void deliverError(VolleyError error) {
        Response.ErrorListener listener;
        synchronized (mLock) {
            listener = mErrorListener;
        }
        if (listener != null) {
            listener.onErrorResponse(error);
        }
    }

RequesQueue

RequesQueue实际上是所有队列的一个管理类,包含正在进行中的队列集合mCurrentRequests,缓存队列mCacheQueue,网络队列mNetworkQueue等

成员变量

//Request添加进去后的序列号
private final AtomicInteger mSequenceGenerator = new AtomicInteger();
//正在进行中的请求集合,采用HashSet实现
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
//请求的缓存队列,采用PriorityBlockingQueue实现,可以根据优先级来出队
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<>();
//请求的网络队列,采用PriorityBlockingQueue实现
private final PriorityBlockingQueue<Request<?>> 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<RequestFinishedListener> mFinishedListeners =new ArrayList<>();

构造方法


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

public RequestQueue(Cache cache, Network network) {
    this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}

//其它的构造方法最终还是间接调用了这个方法
public RequestQueue(Cache cache, Network network, int threadPoolSize,
        ResponseDelivery delivery) {
    mCache = cache;
    mNetwork = network;
    mDispatchers = new NetworkDispatcher[threadPoolSize];
    mDelivery = delivery;
}

通过成员变量的注释,比较清晰,就是默认初始化了一些变量

核心方法

start


public void start() {
    stop();  //终止正在进行的分发器,包括缓存的分发器以及网络分发器
    // 创建缓存分发器
    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
    //启动缓存分发器
    mCacheDispatcher.start();
    // 根据定义的Dispatcher数组,创建网络分发器
    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 (final NetworkDispatcher mDispatcher : mDispatchers) {
            if (mDispatcher != null) {
                mDispatcher.quit();
            }
        }
    }

add

public <T> Request<T> add(Request<T> request) {
    //将RequestQueue赋值给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;
    }
   //添加进缓存队列
    mCacheQueue.add(request);
    return request;
}

cancel

public void cancelAll(final Object tag) {
    if (tag == null) {
        throw new IllegalArgumentException("Cannot cancelAll with a null tag");
    }
    cancelAll(new RequestFilter() {
        @Override
        public boolean apply(Request<?> request) {
        //通过tag来匹配需要取消的请求
            return request.getTag() == tag;
        }
    });
}
//通过RequestFilter来过滤需要取消的请求
  public void cancelAll(RequestFilter filter) {
        synchronized (mCurrentRequests) {
            for (Request<?> request : mCurrentRequests) {
                if (filter.apply(request)) {
                    request.cancel();
                }
            }
        }
    }

finish

<T> void finish(Request<T> request) {
    // 从正在进行的请求中移除
    synchronized (mCurrentRequests) {
        mCurrentRequests.remove(request);
    }
    synchronized (mFinishedListeners) {
      //移除回调接口
        for (RequestFinishedListener<T> listener : mFinishedListeners) {
            listener.onRequestFinished(request);
        }
    }

}

Dispatcher

Volley提供了两个分发器,一个是CacheDispatcher,一个是NetworkDispatcher,实际上就是两个线程,然后进行了死循环,不断地从缓存队列跟网络队列中进行取Request来进行分发。

CacheDispatcher

继承关系
CacheDispatcher
成员变量
//Debug模式的标志
private static final boolean DEBUG = VolleyLog.DEBUG;
//缓存队列,采用BlockingQueue实现生产者消费者模式
private final BlockingQueue<Request<?>> mCacheQueue;
//网络队列,采用BlockingQueue实现生产者消费者模式
private final BlockingQueue<Request<?>> mNetworkQueue;
//缓存类
private final Cache mCache;
//网络请求结果分发类
private final ResponseDelivery mDelivery;
//CacheDispatcher是否退出的标志
private volatile boolean mQuit = false;
//等待管理队列管理器
private final WaitingRequestManager mWaitingRequestManager;

构造方法
public CacheDispatcher(
        BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
        Cache cache, ResponseDelivery delivery) {
    mCacheQueue = cacheQueue;
    mNetworkQueue = networkQueue;
    mCache = cache;
    mDelivery = delivery;
    mWaitingRequestManager = new WaitingRequestManager(this);
}

CacheDispatcher持有cacheQueue,networkQueue,cache,delivery这几个类

run
@Override
public void run() {
    if (DEBUG) VolleyLog.v("start new dispatcher");
    //设置线程优先级
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    //缓存初始化,待会在缓存中具体分析
    mCache.initialize();
    while (true) {
        try {
        //死循环
            processRequest();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                return;
            }
        }
    }
}

processRequest

private void processRequest() throws InterruptedException {
    //从缓存队列中取队列
    final Request<?> request = mCacheQueue.take();
    //给取出的Requet打上标记
    request.addMarker("cache-queue-take");
    //如果请求已取消,结束掉这个请求
    if (request.isCanceled()) {
        request.finish("cache-discard-canceled");
        return;
    }
    //拿到缓存的entry
    Cache.Entry entry = mCache.get(request.getCacheKey());
    //缓存数据为空,就将Request添加进mNetworkQueue
    if (entry == null) {
        request.addMarker("cache-miss");
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            mNetworkQueue.put(request);
        }
        return;
    }

    // 缓存过期,直接加入到网络队列
    if (entry.isExpired()) {
        request.addMarker("cache-hit-expired");
        request.setCacheEntry(entry);
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            mNetworkQueue.put(request);
        }
        return;
    }
    //缓存有效,直接解析发送给主线程
    request.addMarker("cache-hit");
    Response<?> response = request.parseNetworkResponse(
            new NetworkResponse(entry.data, entry.responseHeaders));
    request.addMarker("cache-hit-parsed");

}
quit

退出线程

public void quit() {
    mQuit = true;
   //中断线程
    interrupt();
}

NetworkDispatcher

继承关系
NetworkDispatcher
成员变量
//网络请求队列
private final BlockingQueue<Request<?>> mQueue;
//网络请求的实际操作类
private final Network mNetwork;
//缓存类
private final Cache mCache;
//请求响应的结果发送者
private final ResponseDelivery mDelivery;
//线程是否发送的标志
private volatile boolean mQuit = false;
构造方法
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
        Network network, Cache cache, ResponseDelivery delivery) {
    mQueue = queue;
    mNetwork = network;
    mCache = cache;
    mDelivery = delivery;
}

对比CacheDispatcher,发现少了缓存队列,不过也很好理解,因为既然都到了网络这边了,说明缓存肯定GG了,所以只需要在获取到网络请求结果之后,放入缓存中就行了。

run

run方法其实跟CacheDispatcher是一样的,只是processRequest有些区别

private void processRequest() throws InterruptedException {
    long startTimeMs = SystemClock.elapsedRealtime();
    //从队列中取出一个队列
    Request<?> request = mQueue.take();
    try {
        request.addMarker("network-queue-take");
        //请求取消,直接finished
        if (request.isCanceled()) {
            request.finish("network-discard-cancelled");
            request.notifyListenerResponseNotUsable();
            return;
        }
        addTrafficStatsTag(request);
        // 进行网络请求
        NetworkResponse networkResponse = mNetwork.performRequest(request);
        request.addMarker("network-http-complete");
        // If the server returned 304 AND we delivered a response already,
        // we're done -- don't deliver a second identical response
        if (networkResponse.notModified && request.hasHadResponseDelivered()) {
            request.finish("not-modified");
            request.notifyListenerResponseNotUsable();
            return;
        }
        // 解析网络请求数据
        Response<?> response = request.parseNetworkResponse(networkResponse);
        request.addMarker("network-parse-complete");
        //如果请求结果需要缓存,那么缓存请求的结果
        if (request.shouldCache() && response.cacheEntry != null) {
            mCache.put(request.getCacheKey(), response.cacheEntry);
            request.addMarker("network-cache-written");
        }
        // 将解析好的数据发送给主线程
        request.markDelivered();
        mDelivery.postResponse(request, response);
        request.notifyListenerResponseReceived(response);
    } catch (VolleyError volleyError) {
        volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
        parseAndDeliverNetworkError(request, volleyError);
        request.notifyListenerResponseNotUsable();
    } 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.notifyListenerResponseNotUsable();
    }
}
quit
public void quit() {
    mQuit = true;
    //中断线程
    interrupt();
}

Cache

Volley的缓存主要是磁盘缓存,首先Volley提供了一个Cache接口,然后DiskBasedCache实现了这个接口,下面说一下这两个类

Cache

public interface Cache {
  
    Entry get(String key);
    void put(String key, Entry entry);
    void initialize();
    void invalidate(String key, boolean fullExpire);
    void remove(String key);
    void clear();
    class Entry {
        public byte[] data;
        public String etag;
        public long serverDate;
        public long lastModified;
        public long ttl;
        public long softTtl;
        public Map<String, String> responseHeaders = Collections.emptyMap();
        public List<Header> allResponseHeaders;
        public boolean isExpired() {
            return this.ttl < System.currentTimeMillis();
        }
        public boolean refreshNeeded() {
            return this.softTtl < System.currentTimeMillis();
        }
    }

很常规的接口,只不过缓存的value不是请求的结果,而是封装了请求的数据的一个Entry,可以对缓存做一些判断。

DiskBaseCache

成员变量

//当前缓存的容量
private long mTotalSize = 0;
//缓存的路径
private final File mRootDirectory;
//分配的最大缓存容量
private final int mMaxCacheSizeInBytes;
//默认的最大缓存容量
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
//缓存的负载因子,到达这个点之后会自动进行缓存清理
private static final float HYSTERESIS_FACTOR = 0.9f;
//底层采用LinkedHashMap实现Lru算法,按照使用的顺序进行排序
private final Map<String, CacheHeader> mEntries =
        new LinkedHashMap<String, CacheHeader>(16, .75f, true);
构造方法
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
    mRootDirectory = rootDirectory;
    mMaxCacheSizeInBytes = maxCacheSizeInBytes;
}
public DiskBasedCache(File rootDirectory) {
    this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}

通过缓存的大小跟路径初始化DiskBasedCache

put
 */
@Override
public synchronized void put(String key, Entry entry) {
   //检查容量是否合理,不合理就进行删除
    pruneIfNeeded(entry.data.length);
   //获取缓存的文件
    File file = getFileForKey(key);
 
    try {
        BufferedOutputStream fos = new BufferedOutputStream(createOutputStream(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());
    }
}

pruneIfNeed

private void pruneIfNeeded(int neededSpace) {
    //如果现有容量+即将存储的容量小于最大容量,返回
    if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
        return;
    }
    long before = mTotalSize;
    int prunedFiles = 0;
    long startTime = SystemClock.elapsedRealtime();
    Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();
   //遍历LinkedHashMap,删除链表头部的数据
    while (iterator.hasNext()) {
        Map.Entry<String, CacheHeader> 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;
        }
    }
}
get
@Override
public synchronized Entry get(String key) {
    //通过key获取缓存的entry
    CacheHeader entry = mEntries.get(key);
    //如果entry为null的话直接返回
    if (entry == null) {
        return null;
    }
    //通过key获取到file文件
    File file = getFileForKey(key);
    try {
        CountingInputStream cis = new CountingInputStream(
                new BufferedInputStream(createInputStream(file)), file.length());
        try {
            CacheHeader entryOnDisk = CacheHeader.readHeader(cis);
            if (!TextUtils.equals(key, entryOnDisk.key)) {
                // File was shared by two keys and now holds data for a different entry!
                VolleyLog.d("%s: key=%s, found=%s",
                        file.getAbsolutePath(), key, entryOnDisk.key);
                // Remove key whose contents on disk have been replaced.
                removeEntry(key);
                return null;
            }
            byte[] data = streamToBytes(cis, cis.bytesRemaining());
            //将解析好的数据返回
            return entry.toCacheEntry(data);
        } finally {
            // Any IOException thrown here is handled by the below catch block by design.
            //noinspection ThrowFromFinallyBlock
            cis.close();
        }
    } catch (IOException e) {
        VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
        remove(key);
        return null;
    }
}

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 = 1;//默认重试次数
public static final float DEFAULT_BACKOFF_MULT = 1f;//默认的失败之后重连的间隔因子为1

构造方法


public DefaultRetryPolicy() {
    this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}

public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
    mCurrentTimeoutMs = initialTimeoutMs;
    mMaxNumRetries = maxNumRetries;
    mBackoffMultiplier = backoffMultiplier;
}

传入超时时间,最大重试次数,重试间隔

retry

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

Image

Volley不仅支持网络请求,还可以用来加载图片,主要相关的两个核心类是ImageLoader跟ImageRequest

ImageLoader

成员变量

private final RequestQueue mRequestQueue;//请求队列
private int mBatchResponseDelayMs = 100;//请求响应结果发送延时
private final ImageCache mCache;//图片缓存
//用HashMap来保存延时的请求 
private final HashMap<String, BatchedImageRequest> mInFlightRequests =
        new HashMap<String, BatchedImageRequest>();

//HashMap来保存延时的请求响应结果
private final HashMap<String, BatchedImageRequest> mBatchedResponses =
        new HashMap<String, BatchedImageRequest>();
//切换线程的Handler
private final Handler mHandler = new Handler(Looper.getMainLooper());


构造方法
public ImageLoader(RequestQueue queue, ImageCache imageCache) {
    mRequestQueue = queue;
    mCache = imageCache;
}
get
public ImageContainer get(String requestUrl, final ImageListener listener) {
    return get(requestUrl, listener, 0, 0);
}

间接调用

public ImageContainer get(String requestUrl, ImageListener imageListener,
        int maxWidth, int maxHeight) {
    return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
}

继续调用

public ImageContainer get(String requestUrl, ImageListener imageListener,
        int maxWidth, int maxHeight, ScaleType scaleType) {

    // 检测是否在主线程
    throwIfNotOnMainThread();
   //通过转换得到缓存的key
    final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
    // 从缓存中查找对应的bitmap
    Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
    if (cachedBitmap != null) {
        //找到直接返回
      ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
        imageListener.onResponse(container, true);
        return container;
    }

    // 缓存失败,初始化ImageContainer
    ImageContainer imageContainer =
            new ImageContainer(null, requestUrl, cacheKey, imageListener);

    //回调
    imageListener.onResponse(imageContainer, true);
    // 判断当前的请求是否在mBatchedResponses中
    BatchedImageRequest request = mInFlightRequests.get(cacheKey);
    if (request != null) {
        // If it is, add this request to the list of listeners.
        request.addContainer(imageContainer);
        return imageContainer;
    }
    // 传达
  Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,cacheKey);
    mRequestQueue.add(newRequest);
    mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer));
    return imageContainer;
}

get方法返回的是一个ImageContainer,里面包好了很多跟Image相关的信息,类似Cache,mCacheKey,mRequestUrl,mListener。

ImageRequest

继承关系
ImageRequest

ImageRequest继承自Request,然后定义的泛型是Bitmap

成员变量
//超时时间
public static final int DEFAULT_IMAGE_TIMEOUT_MS = 1000;
//默认的重试次数
public static final int DEFAULT_IMAGE_MAX_RETRIES = 2;
//默认重试延迟因子
public static final float DEFAULT_IMAGE_BACKOFF_MULT = 2f;
private final Object mLock = new Object();//全局对象锁
private Response.Listener<Bitmap> mListener;//回调监听
private final Config mDecodeConfig;//解码的配置信息
private final int mMaxWidth;//ImageView传入的最大宽度
private final int mMaxHeight;//ImageView传入的最大高度
private final ScaleType mScaleType;//缩放类型
private static final Object sDecodeLock = new Object();//解码的同步锁
构造方法

@Deprecated
public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
        Config decodeConfig, Response.ErrorListener errorListener) {
    this(url, listener, maxWidth, maxHeight,
            ScaleType.CENTER_INSIDE, decodeConfig, errorListener);
}

public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
        ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) {
    super(Method.GET, url, errorListener);
    setRetryPolicy(new DefaultRetryPolicy(DEFAULT_IMAGE_TIMEOUT_MS, DEFAULT_IMAGE_MAX_RETRIES,
            DEFAULT_IMAGE_BACKOFF_MULT));
    mListener = listener;
    mDecodeConfig = decodeConfig;
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;
    mScaleType = scaleType;
}

构造方法里面都是一些配置信息,没什么好说的

cancel
@Override
public void cancel() {
    super.cancel();
    synchronized (mLock) {
        mListener = null;
    }
}

跟前面的一个套路,不解释

doParse

网络请求回来之后,经过传递最终到了doParse方法

private Response<Bitmap> doParse(NetworkResponse response) {
   //拿到字节数组
    byte[] data = response.data;
    BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
    Bitmap bitmap = null;
    if (mMaxWidth == 0 && mMaxHeight == 0) {
       //传入的宽高都为0,不缩放,直接返回原始尺寸
        decodeOptions.inPreferredConfig = mDecodeConfig;
        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
    } else {
        // If we have to resize this image, first get the natural bounds.
        //先不加载进内存
        decodeOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
        //获取实际宽高
        int actualWidth = decodeOptions.outWidth;
        int actualHeight = decodeOptions.outHeight;
        // 进行比例缩放,获取时间宽高
        int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                actualWidth, actualHeight, mScaleType);
        int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                actualHeight, actualWidth, mScaleType);

        // 进行缩放
        decodeOptions.inJustDecodeBounds = false;
        decodeOptions.inSampleSize =
            findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
        Bitmap tempBitmap =
            BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
        // 如果有必要的话,把得到的bitmap的最大边进行压缩来适应尺寸
        if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
                tempBitmap.getHeight() > desiredHeight)) {
            bitmap = Bitmap.createScaledBitmap(tempBitmap,
                    desiredWidth, desiredHeight, true);
            tempBitmap.recycle();
        } else {
            bitmap = tempBitmap;
        }
    }
    if (bitmap == null) {
    //解析失败回调
        return Response.error(new ParseError(response));
    } else {
    //解析成功回调
        return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
    }
}

Volley加载图片的大致流程就到了这里,可能会有些奇怪,Volley并没有采用Lrucache在内存中进行缓存,是因为ImageRequest继承自Request,所以就依赖于缓存队列,只有File的缓存,可能这也是为什么提到图片加载大家可能会想到很多的Fresco,Glide,Picasso,但是很少人会想到Volley,提到Volley想到的还是网络请求,没有LRUCache应该是最主要的原因了。

总结

Volley是一款扩展性很强的框架,抽取了Request基类,用户可以自定义任意的Request,底层并没有使用线程池,而是采用了四个网络线程从RequestQueue中取数据,如果是数据量较小的网络请求,使用起来比较灵活,如果网络请求比较耗时,那么Volley的四个线程可能就不够用了,我们可以创建更多的线程,但是线程的开销会很高,而且对线程的利用率不大,这个时候就需要使用线程池了。Volley提供图片加载的功能,但是没有实现内存缓存,所以性能不是很高。Volley原生没有提供图片上传功能,不过由于他的扩展性很好,所以我们可以自己继承Request类来实现这个功能。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容