Architecture(2)Volley源碼分析

概述

Volley是Google推出的一款比較輕巧的網(wǎng)絡(luò)請(qǐng)求框架,并且可以對(duì)請(qǐng)求進(jìn)行緩存,同時(shí)可以實(shí)時(shí)取消請(qǐng)求,設(shè)置請(qǐng)求優(yōu)先級(jí),內(nèi)置了ImageRequest,JsonRequest,JsonObjectRequest,JsonArrayRequest,StringRequest等,并且還支持自定義Request,基本上能滿足日常的開發(fā),當(dāng)讓Volley原生并不支持文件上傳,但是可以通過(guò)自定義Request來(lái)實(shí)現(xiàn),Volley不僅僅支持網(wǎng)絡(luò)請(qǐng)求,還支持圖片加載,這個(gè)主要是通過(guò)內(nèi)置的ImageRequest來(lái)實(shí)現(xiàn),Volley的工作原理大致如下:

Volley_final

大致流程就是,當(dāng)添加一個(gè)Request的時(shí)候,首先會(huì)被添加到CachaQueue中,

正文

工作流程

flow

Volley的緩存跟常規(guī)的緩存不太一致,它并不是直接去取緩存,而是構(gòu)造了一個(gè)緩存隊(duì)列,存放Request,然后根據(jù)特有的key值去取緩存,如果緩存存在并且沒(méi)有過(guò)期,請(qǐng)求也沒(méi)有取消,那么就直接解析緩存數(shù)據(jù),發(fā)送到主線程,不然就直接加入到網(wǎng)絡(luò)請(qǐng)求隊(duì)列,重新請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù),Volley的源碼比較多,下面主要是從RequestRequestQueue,Dispatcher,Cache,這幾個(gè)類分析一下Volley的一些實(shí)現(xiàn)細(xì)節(jié),畢竟大部分框架,原理都是一兩句話都能說(shuō)清楚,但是有很多細(xì)節(jié)讓自己實(shí)現(xiàn)其實(shí)還是挺困難的。

Request

繼承關(guān)系

Request

Request是一個(gè)單獨(dú)的類,實(shí)現(xiàn)了Comparable接口,主要是用來(lái)對(duì)請(qǐng)求進(jìn)行排序,如果設(shè)置了請(qǐng)求的優(yōu)先級(jí),就會(huì)根據(jù)優(yōu)先級(jí)來(lái)進(jìn)行排序,如果沒(méi)有優(yōu)先級(jí)就會(huì)按照請(qǐng)求加入的順序來(lái)排序。

成員變量


private final int mMethod;//請(qǐng)求類型,GET,POST
private final String mUrl;//請(qǐng)求的服務(wù)器地址
private final Object mLock = new Object();//用來(lái)給對(duì)象上鎖
private Response.ErrorListener mErrorListener;//請(qǐng)求失敗的監(jiān)聽
private Integer mSequence;//請(qǐng)求的序列號(hào),按照請(qǐng)求的順序依次遞增
private RequestQueue mRequestQueue;//請(qǐng)求隊(duì)列
private boolean mShouldCache = true;//是否需要緩存,默認(rèn)開啟緩存
private boolean mCanceled = false;//請(qǐng)求是否取消,默認(rèn)為false
private boolean mResponseDelivered = false;//解析完的請(qǐng)求是否已經(jīng)發(fā)送
private boolean mShouldRetryServerErrors = false;//遇到服務(wù)器異常是否需要重試
private RetryPolicy mRetryPolicy;//重試策略
private Cache.Entry mCacheEntry = null;//緩存的 對(duì)象,里面封裝了很多跟緩存相關(guān)的信息
private Object mTag;//請(qǐng)求的tag
private NetworkRequestCompleteListener mRequestCompleteListener;//網(wǎng)絡(luò)請(qǐng)求完成回調(diào)的結(jié)果
//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;
}

由于方法的辨識(shí)度比較高,所以Volley沒(méi)有采用枚舉,而是采用了接口內(nèi)定義變量,節(jié)省開銷

Priority

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

優(yōu)先級(jí)更注重可讀性,所以Volley采用了枚舉

構(gòu)造方法

@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

關(guān)鍵方法

cancel

取消請(qǐng)求

public void cancel() {
    synchronized (mLock) {
        mCanceled = true;//改變請(qǐng)求標(biāo)志位
        mErrorListener = null;//回調(diào)接口置空
    }
}

compareTo

設(shè)置請(qǐng)求優(yōu)先級(jí)

@Override
public int compareTo(Request<T> other) {
   //獲取請(qǐng)求優(yōu)先級(jí)
    Priority left = this.getPriority();
    Priority right = other.getPriority();
    //請(qǐng)求優(yōu)先級(jí)默認(rèn)為Normal
    //1.先比較請(qǐng)求優(yōu)先級(jí),如果相等再比較請(qǐng)求加入的順序
    return left == right ?
            this.mSequence - other.mSequence :
            right.ordinal() - left.ordinal();
}

finish

通知RequestQueue,這個(gè)請(qǐng)求已經(jīng)結(jié)束

void finish(final String tag) {
    if (mRequestQueue != null) {
     //通知隊(duì)列移除當(dāng)前請(qǐng)求
        mRequestQueue.finish(this);
    }
    if (MarkerLog.ENABLED) {
        final long threadId = Thread.currentThread().getId();
        //判斷當(dāng)前線程是否為主線程,不是的話切換到主線程
        if (Looper.myLooper() != Looper.getMainLooper()) {
          //通過(guò)PostRunnable的方式,保證請(qǐng)求結(jié)束的打印時(shí)間是有序的
            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

默認(rèn)作為請(qǐng)求的服務(wù)器地址作為key,實(shí)際開發(fā)過(guò)程中需要通過(guò)MD5會(huì)比較好一點(diǎn)

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

抽象方法

Rquest是一個(gè)抽象類,里面還有很多抽象犯法需要子類去實(shí)現(xiàn)

//解析網(wǎng)絡(luò)請(qǐng)求
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
//傳遞網(wǎng)絡(luò)請(qǐng)求結(jié)果
abstract protected void deliverResponse(T response);
//請(qǐng)求失敗的回調(diào),因?yàn)椴煌腞equest需要的返回類型不一樣,需要子類實(shí)現(xiàn),但是請(qǐng)求失敗確是共同的
//所以Volley做了一些封裝
public void deliverError(VolleyError error) {
        Response.ErrorListener listener;
        synchronized (mLock) {
            listener = mErrorListener;
        }
        if (listener != null) {
            listener.onErrorResponse(error);
        }
    }

RequesQueue

RequesQueue實(shí)際上是所有隊(duì)列的一個(gè)管理類,包含正在進(jìn)行中的隊(duì)列集合mCurrentRequests,緩存隊(duì)列mCacheQueue,網(wǎng)絡(luò)隊(duì)列mNetworkQueue等

成員變量

//Request添加進(jìn)去后的序列號(hào)
private final AtomicInteger mSequenceGenerator = new AtomicInteger();
//正在進(jìn)行中的請(qǐng)求集合,采用HashSet實(shí)現(xiàn)
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
//請(qǐng)求的緩存隊(duì)列,采用PriorityBlockingQueue實(shí)現(xiàn),可以根據(jù)優(yōu)先級(jí)來(lái)出隊(duì)
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<>();
//請(qǐng)求的網(wǎng)絡(luò)隊(duì)列,采用PriorityBlockingQueue實(shí)現(xiàn)
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<>();
//網(wǎng)絡(luò)請(qǐng)求分發(fā)器的數(shù)量,默認(rèn)為4個(gè)
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
//數(shù)據(jù)的緩存
private final Cache mCache;
//網(wǎng)絡(luò)請(qǐng)求的實(shí)際執(zhí)行者
private final Network mNetwork;
//網(wǎng)絡(luò)請(qǐng)求返回結(jié)果的分發(fā)者
private final ResponseDelivery mDelivery;
//網(wǎng)絡(luò)請(qǐng)求分發(fā)器數(shù)組
private final NetworkDispatcher[] mDispatchers;
//緩存分發(fā)器線程
private CacheDispatcher mCacheDispatcher;
//網(wǎng)絡(luò)請(qǐng)求完成的監(jiān)聽器集合
private final List<RequestFinishedListener> mFinishedListeners =new ArrayList<>();

構(gòu)造方法


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

//其它的構(gòu)造方法最終還是間接調(diào)用了這個(gè)方法
public RequestQueue(Cache cache, Network network, int threadPoolSize,
        ResponseDelivery delivery) {
    mCache = cache;
    mNetwork = network;
    mDispatchers = new NetworkDispatcher[threadPoolSize];
    mDelivery = delivery;
}

通過(guò)成員變量的注釋,比較清晰,就是默認(rèn)初始化了一些變量

核心方法

start


public void start() {
    stop();  //終止正在進(jìn)行的分發(fā)器,包括緩存的分發(fā)器以及網(wǎng)絡(luò)分發(fā)器
    // 創(chuàng)建緩存分發(fā)器
    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
    //啟動(dòng)緩存分發(fā)器
    mCacheDispatcher.start();
    // 根據(jù)定義的Dispatcher數(shù)組,創(chuàng)建網(wǎng)絡(luò)分發(fā)器
    for (int i = 0; i < mDispatchers.length; i++) {
    NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery);
        mDispatchers[i] = networkDispatcher;
      //啟動(dòng)網(wǎng)絡(luò)分發(fā)器
        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);
    //同步添加到正在進(jìn)行中的請(qǐng)求集合中去
    synchronized (mCurrentRequests) {
        mCurrentRequests.add(request);
    }
    //給請(qǐng)求設(shè)置序列號(hào)
    request.setSequence(getSequenceNumber());
    //添加Marker標(biāo)記位
    request.addMarker("add-to-queue");
    if (!request.shouldCache()) {
     //如果請(qǐng)求隊(duì)列不需要緩存,那么直接加入到網(wǎng)絡(luò)對(duì)壘中
        mNetworkQueue.add(request);
        return request;
    }
   //添加進(jìn)緩存隊(duì)列
    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) {
        //通過(guò)tag來(lái)匹配需要取消的請(qǐng)求
            return request.getTag() == tag;
        }
    });
}
//通過(guò)RequestFilter來(lái)過(guò)濾需要取消的請(qǐng)求
  public void cancelAll(RequestFilter filter) {
        synchronized (mCurrentRequests) {
            for (Request<?> request : mCurrentRequests) {
                if (filter.apply(request)) {
                    request.cancel();
                }
            }
        }
    }

finish

<T> void finish(Request<T> request) {
    // 從正在進(jìn)行的請(qǐng)求中移除
    synchronized (mCurrentRequests) {
        mCurrentRequests.remove(request);
    }
    synchronized (mFinishedListeners) {
      //移除回調(diào)接口
        for (RequestFinishedListener<T> listener : mFinishedListeners) {
            listener.onRequestFinished(request);
        }
    }

}

Dispatcher

Volley提供了兩個(gè)分發(fā)器,一個(gè)是CacheDispatcher,一個(gè)是NetworkDispatcher,實(shí)際上就是兩個(gè)線程,然后進(jìn)行了死循環(huán),不斷地從緩存隊(duì)列跟網(wǎng)絡(luò)隊(duì)列中進(jìn)行取Request來(lái)進(jìn)行分發(fā)。

CacheDispatcher

繼承關(guān)系
CacheDispatcher
成員變量
//Debug模式的標(biāo)志
private static final boolean DEBUG = VolleyLog.DEBUG;
//緩存隊(duì)列,采用BlockingQueue實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式
private final BlockingQueue<Request<?>> mCacheQueue;
//網(wǎng)絡(luò)隊(duì)列,采用BlockingQueue實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式
private final BlockingQueue<Request<?>> mNetworkQueue;
//緩存類
private final Cache mCache;
//網(wǎng)絡(luò)請(qǐng)求結(jié)果分發(fā)類
private final ResponseDelivery mDelivery;
//CacheDispatcher是否退出的標(biāo)志
private volatile boolean mQuit = false;
//等待管理隊(duì)列管理器
private final WaitingRequestManager mWaitingRequestManager;

構(gòu)造方法
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這幾個(gè)類

run
@Override
public void run() {
    if (DEBUG) VolleyLog.v("start new dispatcher");
    //設(shè)置線程優(yōu)先級(jí)
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    //緩存初始化,待會(huì)在緩存中具體分析
    mCache.initialize();
    while (true) {
        try {
        //死循環(huán)
            processRequest();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                return;
            }
        }
    }
}

processRequest

private void processRequest() throws InterruptedException {
    //從緩存隊(duì)列中取隊(duì)列
    final Request<?> request = mCacheQueue.take();
    //給取出的Requet打上標(biāo)記
    request.addMarker("cache-queue-take");
    //如果請(qǐng)求已取消,結(jié)束掉這個(gè)請(qǐng)求
    if (request.isCanceled()) {
        request.finish("cache-discard-canceled");
        return;
    }
    //拿到緩存的entry
    Cache.Entry entry = mCache.get(request.getCacheKey());
    //緩存數(shù)據(jù)為空,就將Request添加進(jìn)mNetworkQueue
    if (entry == null) {
        request.addMarker("cache-miss");
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            mNetworkQueue.put(request);
        }
        return;
    }

    // 緩存過(guò)期,直接加入到網(wǎng)絡(luò)隊(duì)列
    if (entry.isExpired()) {
        request.addMarker("cache-hit-expired");
        request.setCacheEntry(entry);
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            mNetworkQueue.put(request);
        }
        return;
    }
    //緩存有效,直接解析發(fā)送給主線程
    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

繼承關(guān)系
NetworkDispatcher
成員變量
//網(wǎng)絡(luò)請(qǐng)求隊(duì)列
private final BlockingQueue<Request<?>> mQueue;
//網(wǎng)絡(luò)請(qǐng)求的實(shí)際操作類
private final Network mNetwork;
//緩存類
private final Cache mCache;
//請(qǐng)求響應(yīng)的結(jié)果發(fā)送者
private final ResponseDelivery mDelivery;
//線程是否發(fā)送的標(biāo)志
private volatile boolean mQuit = false;
構(gòu)造方法
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
        Network network, Cache cache, ResponseDelivery delivery) {
    mQueue = queue;
    mNetwork = network;
    mCache = cache;
    mDelivery = delivery;
}

對(duì)比CacheDispatcher,發(fā)現(xiàn)少了緩存隊(duì)列,不過(guò)也很好理解,因?yàn)榧热欢嫉搅司W(wǎng)絡(luò)這邊了,說(shuō)明緩存肯定GG了,所以只需要在獲取到網(wǎng)絡(luò)請(qǐng)求結(jié)果之后,放入緩存中就行了。

run

run方法其實(shí)跟CacheDispatcher是一樣的,只是processRequest有些區(qū)別

private void processRequest() throws InterruptedException {
    long startTimeMs = SystemClock.elapsedRealtime();
    //從隊(duì)列中取出一個(gè)隊(duì)列
    Request<?> request = mQueue.take();
    try {
        request.addMarker("network-queue-take");
        //請(qǐng)求取消,直接finished
        if (request.isCanceled()) {
            request.finish("network-discard-cancelled");
            request.notifyListenerResponseNotUsable();
            return;
        }
        addTrafficStatsTag(request);
        // 進(jìn)行網(wǎng)絡(luò)請(qǐng)求
        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;
        }
        // 解析網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)
        Response<?> response = request.parseNetworkResponse(networkResponse);
        request.addMarker("network-parse-complete");
        //如果請(qǐng)求結(jié)果需要緩存,那么緩存請(qǐng)求的結(jié)果
        if (request.shouldCache() && response.cacheEntry != null) {
            mCache.put(request.getCacheKey(), response.cacheEntry);
            request.addMarker("network-cache-written");
        }
        // 將解析好的數(shù)據(jù)發(fā)送給主線程
        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提供了一個(gè)Cache接口,然后DiskBasedCache實(shí)現(xiàn)了這個(gè)接口,下面說(shuō)一下這兩個(gè)類

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

很常規(guī)的接口,只不過(guò)緩存的value不是請(qǐng)求的結(jié)果,而是封裝了請(qǐng)求的數(shù)據(jù)的一個(gè)Entry,可以對(duì)緩存做一些判斷。

DiskBaseCache

成員變量

//當(dāng)前緩存的容量
private long mTotalSize = 0;
//緩存的路徑
private final File mRootDirectory;
//分配的最大緩存容量
private final int mMaxCacheSizeInBytes;
//默認(rèn)的最大緩存容量
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
//緩存的負(fù)載因子,到達(dá)這個(gè)點(diǎn)之后會(huì)自動(dòng)進(jìn)行緩存清理
private static final float HYSTERESIS_FACTOR = 0.9f;
//底層采用LinkedHashMap實(shí)現(xiàn)Lru算法,按照使用的順序進(jìn)行排序
private final Map<String, CacheHeader> mEntries =
        new LinkedHashMap<String, CacheHeader>(16, .75f, true);
構(gòu)造方法
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
    mRootDirectory = rootDirectory;
    mMaxCacheSizeInBytes = maxCacheSizeInBytes;
}
public DiskBasedCache(File rootDirectory) {
    this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}

通過(guò)緩存的大小跟路徑初始化DiskBasedCache

put
 */
@Override
public synchronized void put(String key, Entry entry) {
   //檢查容量是否合理,不合理就進(jìn)行刪除
    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();
      //緩存數(shù)據(jù)
        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) {
    //如果現(xiàn)有容量+即將存儲(chǔ)的容量小于最大容量,返回
    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,刪除鏈表頭部的數(shù)據(jù)
    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) {
    //通過(guò)key獲取緩存的entry
    CacheHeader entry = mEntries.get(key);
    //如果entry為null的話直接返回
    if (entry == null) {
        return null;
    }
    //通過(guò)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());
            //將解析好的數(shù)據(jù)返回
            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;//超時(shí)時(shí)間
private int mCurrentRetryCount;//已重試次數(shù)
private final int mMaxNumRetries;//最大重試次數(shù)
private final float mBackoffMultiplier;//失敗后重連的間隔因子
public static final int DEFAULT_TIMEOUT_MS = 2500;//默認(rèn)超時(shí)時(shí)間
public static final int DEFAULT_MAX_RETRIES = 1;//默認(rèn)重試次數(shù)
public static final float DEFAULT_BACKOFF_MULT = 1f;//默認(rèn)的失敗之后重連的間隔因子為1

構(gòu)造方法


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

傳入超時(shí)時(shí)間,最大重試次數(shù),重試間隔

retry

@Override
public void retry(VolleyError error) throws VolleyError {
    mCurrentRetryCount++;
    //計(jì)算重試時(shí)間
    mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
    if (!hasAttemptRemaining()) {
      //如果到達(dá)最大次數(shù),還是失敗就拋異常
        throw error;
    }
}

Image

Volley不僅支持網(wǎng)絡(luò)請(qǐng)求,還可以用來(lái)加載圖片,主要相關(guān)的兩個(gè)核心類是ImageLoader跟ImageRequest

ImageLoader

成員變量

private final RequestQueue mRequestQueue;//請(qǐng)求隊(duì)列
private int mBatchResponseDelayMs = 100;//請(qǐng)求響應(yīng)結(jié)果發(fā)送延時(shí)
private final ImageCache mCache;//圖片緩存
//用HashMap來(lái)保存延時(shí)的請(qǐng)求 
private final HashMap<String, BatchedImageRequest> mInFlightRequests =
        new HashMap<String, BatchedImageRequest>();

//HashMap來(lái)保存延時(shí)的請(qǐng)求響應(yīng)結(jié)果
private final HashMap<String, BatchedImageRequest> mBatchedResponses =
        new HashMap<String, BatchedImageRequest>();
//切換線程的Handler
private final Handler mHandler = new Handler(Looper.getMainLooper());


構(gòu)造方法
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);
}

間接調(diào)用

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

繼續(xù)調(diào)用

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

    // 檢測(cè)是否在主線程
    throwIfNotOnMainThread();
   //通過(guò)轉(zhuǎn)換得到緩存的key
    final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
    // 從緩存中查找對(duì)應(yīng)的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);

    //回調(diào)
    imageListener.onResponse(imageContainer, true);
    // 判斷當(dāng)前的請(qǐng)求是否在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;
    }
    // 傳達(dá)
  Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,cacheKey);
    mRequestQueue.add(newRequest);
    mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer));
    return imageContainer;
}

get方法返回的是一個(gè)ImageContainer,里面包好了很多跟Image相關(guān)的信息,類似Cache,mCacheKey,mRequestUrl,mListener。

ImageRequest

繼承關(guān)系
ImageRequest

ImageRequest繼承自Request,然后定義的泛型是Bitmap

成員變量
//超時(shí)時(shí)間
public static final int DEFAULT_IMAGE_TIMEOUT_MS = 1000;
//默認(rèn)的重試次數(shù)
public static final int DEFAULT_IMAGE_MAX_RETRIES = 2;
//默認(rèn)重試延遲因子
public static final float DEFAULT_IMAGE_BACKOFF_MULT = 2f;
private final Object mLock = new Object();//全局對(duì)象鎖
private Response.Listener<Bitmap> mListener;//回調(diào)監(jiān)聽
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();//解碼的同步鎖
構(gòu)造方法

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

構(gòu)造方法里面都是一些配置信息,沒(méi)什么好說(shuō)的

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

跟前面的一個(gè)套路,不解釋

doParse

網(wǎng)絡(luò)請(qǐng)求回來(lái)之后,經(jīng)過(guò)傳遞最終到了doParse方法

private Response<Bitmap> doParse(NetworkResponse response) {
   //拿到字節(jié)數(shù)組
    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.
        //先不加載進(jìn)內(nèi)存
        decodeOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
        //獲取實(shí)際寬高
        int actualWidth = decodeOptions.outWidth;
        int actualHeight = decodeOptions.outHeight;
        // 進(jìn)行比例縮放,獲取時(shí)間寬高
        int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                actualWidth, actualHeight, mScaleType);
        int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                actualHeight, actualWidth, mScaleType);

        // 進(jìn)行縮放
        decodeOptions.inJustDecodeBounds = false;
        decodeOptions.inSampleSize =
            findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
        Bitmap tempBitmap =
            BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
        // 如果有必要的話,把得到的bitmap的最大邊進(jìn)行壓縮來(lái)適應(yīng)尺寸
        if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
                tempBitmap.getHeight() > desiredHeight)) {
            bitmap = Bitmap.createScaledBitmap(tempBitmap,
                    desiredWidth, desiredHeight, true);
            tempBitmap.recycle();
        } else {
            bitmap = tempBitmap;
        }
    }
    if (bitmap == null) {
    //解析失敗回調(diào)
        return Response.error(new ParseError(response));
    } else {
    //解析成功回調(diào)
        return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
    }
}

Volley加載圖片的大致流程就到了這里,可能會(huì)有些奇怪,Volley并沒(méi)有采用Lrucache在內(nèi)存中進(jìn)行緩存,是因?yàn)镮mageRequest繼承自Request,所以就依賴于緩存隊(duì)列,只有File的緩存,可能這也是為什么提到圖片加載大家可能會(huì)想到很多的Fresco,Glide,Picasso,但是很少人會(huì)想到Volley,提到Volley想到的還是網(wǎng)絡(luò)請(qǐng)求,沒(méi)有LRUCache應(yīng)該是最主要的原因了。

總結(jié)

Volley是一款擴(kuò)展性很強(qiáng)的框架,抽取了Request基類,用戶可以自定義任意的Request,底層并沒(méi)有使用線程池,而是采用了四個(gè)網(wǎng)絡(luò)線程從RequestQueue中取數(shù)據(jù),如果是數(shù)據(jù)量較小的網(wǎng)絡(luò)請(qǐng)求,使用起來(lái)比較靈活,如果網(wǎng)絡(luò)請(qǐng)求比較耗時(shí),那么Volley的四個(gè)線程可能就不夠用了,我們可以創(chuàng)建更多的線程,但是線程的開銷會(huì)很高,而且對(duì)線程的利用率不大,這個(gè)時(shí)候就需要使用線程池了。Volley提供圖片加載的功能,但是沒(méi)有實(shí)現(xiàn)內(nèi)存緩存,所以性能不是很高。Volley原生沒(méi)有提供圖片上傳功能,不過(guò)由于他的擴(kuò)展性很好,所以我們可以自己繼承Request類來(lái)實(shí)現(xiàn)這個(gè)功能。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容