Volley框架源碼解析

LowMerlin 8年前發布 | 7K 次閱讀 Volley Android開發 移動開發

0001B

在 2013 年的 Google I/O 大會上,Volley 網絡通信框架正式發布。Volley 框架被設計為適用于網絡請求非常頻繁但是數據量并不是特別大的情景,正如它的名字一樣。Volley 相比其他網絡框架而言,采用了在 Android 2.3 以下使用 HttpClient ,而 Android 2.3 及以上使用 HttpUrlConnection 的方案。這是因為在 Android 2.3 以下時,HttpUrlConnection 并不完善,有很多 bug 存在。因此在 Android 2.3 以下最好使用 HttpClient 來進行網絡通信;而在 Android 2.3 及以上,HttpUrlConnection 比起 HttpClient 來說更加簡單易用,修復了之前的 bug 。所以在 Android 2.3 及以上我們使用 HttpUrlConnection 來進行網絡通信。

除此之外,Volley 框架還具有優先級處理、可擴展性強等特點。雖然現在有 Retrofit 、OkHttp 等十分優秀的網絡通信框架,但是深入理解 Volley 框架內部的思想可以大大提高我們自身的技術水平,畢竟僅僅停留在只會使用的階段是不行的哦。那么,下面就進入我們今天的正題吧!( ps :本文篇幅過長,可能會引起不適,請在家長的陪同下觀看)

0010B

Volley 使用方法

在長篇大論地解析 Volley 框架源碼之前,我們先來看看平時是怎樣使用 Volley 的。(大牛可直接跳過 -_- )

RequestQueue mQueue = Volley.newRequestQueue(context);
JsonObjectRequest request = new JsonObjectRequest(url, null,
        new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject jsonObject) {
                // TODO 
            }
        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError volleyError) {
        // TODO 
    }
});
mQueue.add(request);

我們通過 Volley.newRequestQueue(context) 來得到一個請求隊列的對象 mQueue ,在隊列中暫存了我們所有 add 進去的 request ,之后一個個取出 request 來進行網絡通信。一般來說,在一個應用程序中,只保持一個請求隊列的對象。

之后創建了 JsonObjectRequest 對象用來請求 JSON 數據,并把它加入 mQueue 的隊列中。Volley 框架的使用方法非常簡單,并且有多種 request 請求方式可以選擇,使用方法都是和上面類似的。

0011B

在這先把 Volley 框架中幾個重要的類的作用講一下,以便看源碼時能夠更加明白:

  • RequestQueue :這個大家一看都明白,用來緩存 request 的請求隊列,根據優先級高低排列;
  • Request :表示網絡請求,本身是一個抽象類,子類有 StringRequest 、JsonRequest 、ImageRequest 等;
  • Response :表示網絡請求后的響應,也是一個抽象類。內部定義了 Listener 、ErrorListener 接口;
  • NetworkResponse :對返回的 HttpResponse 內容進行了封裝,雖然類名和 Response 差不多,但是不是 Response 的子類;
  • CacheDispatcher :一個處理請求緩存的線程。不斷從 RequestQueue 中取出 Request ,然后取得該 Request 對應的緩存,若緩存存在就調用 ResponseDelivery 做后續分發處理;如果沒有緩存或者緩存失效需要進入 NetworkDispatcher 中從網絡上獲取結果;
  • NetworkDispatcher :一個處理網絡請求的線程。和 CacheDispatcher 類似,從網絡上得到響應后調用 ResponseDelivery 做后續分發處理。而且根據需求判斷是否需要做緩存處理;
  • ResponseDelivery :用作分發處理。利用 Handler 把結果回調到主線程中,即 Listener 、ErrorListener 接口。主要實現類為 ExecutorDelivery ;
  • HttpStack :主要作用就是發起 Http 請求。子類分為 HurlStack 和 HttpClientStack ,分別對應著 HttpUrlConnection 和 HttpClient ;
  • Network :處理 Stack 發起的 Http 請求,把 Request 轉換為 Response ,主要實現類為 BasicNetwork ;
  • RetryPolicy :請求重試策略。主要實現類為 DefaultRetryPolicy ;
  • Cache :網絡請求的緩存。在 CacheDispatcher 中獲取 Cache ,在 NetworkDispatcher 中判斷是否保存 Cache 。主要實現類為 DiskBasedCache ,緩存在磁盤中。

Volley

看完了之后,我們就要開始源碼解析。我們入手點就是 Volley.newRequestQueue(context) 了。

public class Volley {

/** 默認的磁盤緩存目錄名 */
private static final String DEFAULT_CACHE_DIR = "volley";

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
    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) {
    }
    // 根據 Android SDK 版本設置 HttpStack ,分為 HurlStack 和 HttpClientStack
    // 分別對應著 HttpUrlConnection 和 HttpClient
    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 network = new BasicNetwork(stack);
    // 創建 RequestQueue
    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    queue.start();

    return queue;
}

public static RequestQueue newRequestQueue(Context context) {
    return newRequestQueue(context, null);
}

}</code></pre>

從上面 Volley 類的源碼中可知,Volley 類主要就是用來創建 RequestQueue 的。我們之前使用的 newRequestQueue(Context context) 方法最終會調用 newRequestQueue(Context context, HttpStack stack) 。Volley 允許我們使用自定義的 HttpStack ,從這也可以看出 Volley 具有很強的擴展性。

RequestQueue

接下來繼續跟蹤 RequestQueue 構造方法的代碼。

// 默認線程池數量為 4
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

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

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

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

在構造方法中創建了 ExecutorDelivery 對象,ExecutorDelivery 中傳入的 Handler 為主線程的,方便得到 Response 后回調;NetworkDispatcher[] 數組對象,默認數組的長度為 4 ,也就意味著默認處理請求的線程最多為 4 個。

在 Volley.newRequestQueue(Context context, HttpStack stack) 中創建完 RequestQueue 對象 queue 之后,還調用了 queue.start() 方法。主要用于啟動 queue 中的 mCacheDispatcher 和 mDispatchers 。

/* 請求緩存隊列 /
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
    new PriorityBlockingQueue<Request<?>>();

/* 網絡請求隊列 / private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();

public void start() { stop(); // 確保當前 RequestQueue 中的 mCacheDispatcher 和 mDispatchers[] 是關閉的 // 創建 mCacheDispatcher ,并且開啟 mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start();

// 根據 mDispatchers[] 數組的長度創建 networkDispatcher ,并且開啟
for (int i = 0; i < mDispatchers.length; i++) {
    NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
            mCache, mDelivery);
    mDispatchers[i] = networkDispatcher;
    networkDispatcher.start();
}

}

// 關閉當前的 mCacheDispatcher 和 mDispatchers[] public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (int i = 0; i < mDispatchers.length; i++) { if (mDispatchers[i] != null) { mDispatchers[i].quit(); } } }

void finish(Request<?> request) { // Remove from the set of requests currently being processed. synchronized (mCurrentRequests) { // 從 mCurrentRequests 中移除該 request mCurrentRequests.remove(request); } // 如果 request 是可以被緩存的,那么從 mWaitingRequests 中移除,加入到 mCacheQueue 中
if (request.shouldCache()) { synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey); if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", waitingRequests.size(), cacheKey); } // 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); } } } }</code></pre>

那么看到這里我們意識到有必要看一下 CacheDispatcher 和 NetworkDispatcher 的代碼。我們先暫且放一下,來看看 RequestQueue 的 add 方法。 add 方法就是把 Request 加入到 RequestQueue 中了:

private final Map<String, Queue<Request<?>>> mWaitingRequests =
        new HashMap<String, Queue<Request<?>>>();

// 當前正在請求的 Set 集合 private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();

public <T> Request<T> add(Request<T> request) { // Tag the request as belonging to this queue and add it to the set of current requests. request.setRequestQueue(this); synchronized (mCurrentRequests) { //加入到當前請求隊列中 mCurrentRequests.add(request); }

// 設置序列號,該序列號為 AtomInteger 自增的值
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");

// 如果該 request 不該緩存,則直接加入 mNetworkQueue ,跳過下面的步驟
if (!request.shouldCache()) {
    mNetworkQueue.add(request);
    return request;
}

synchronized (mWaitingRequests) {
    // 其實 cacheKey 就是 request 的 url
    String cacheKey = request.getCacheKey();
    // 如果該 mWaitingRequests 已經包含了有該 cacheKey
    if (mWaitingRequests.containsKey(cacheKey)) {
        // 得到該 cacheKey 對應的 Queue
        Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
        if (stagedRequests == null) {
            stagedRequests = new LinkedList<Request<?>>();
        }
        stagedRequests.add(request);
        // 把該 request 加入到 mWaitingRequests
        mWaitingRequests.put(cacheKey, stagedRequests);
        if (VolleyLog.DEBUG) {
            VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
        }
    } else {
        // 如果沒有,那么將該 request 加入到 mCacheQueue 中
        mWaitingRequests.put(cacheKey, null);
        mCacheQueue.add(request);
    }
    return request;
}

}</code></pre>

在 add(Request<T> request) 方法中,額外使用了兩個集合來維護 Request ,其中

  • mCurrentRequests :用來維護正在做請求操作的 Request;
  • mWaitingRequests :主要作用是如果當前有一個 Request 正在請求并且是可以緩存的,那么 Volley 會去 mWaitingRequests 中根據該 cacheKey 查詢之前有沒有一樣的 Request 被加入到 mWaitingRequests 中。若有,那么該 Request 就不需要再被緩存了;若沒有就加入到 mCacheQueue 中進行后續操作。

現在我們來看看 CacheDispatcher 和 NetworkDispatcher 類的源碼。

CacheDispatcher

首先是 CacheDispatcher 的:

public class CacheDispatcher extends Thread {

...... // 省略部分源碼

// 結束當前線程
public void quit() {
    mQuit = true;
    interrupt();
}

@Override
public void run() {
    if (DEBUG) VolleyLog.v("start new dispatcher");
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

    // 初始化 mCache ,讀取磁盤中的緩存文件,加載到 mCache 中的 map 中
    // 會造成線程阻塞,要在子線程中調用
    mCache.initialize();

    while (true) {
        try {
            // 從緩存隊列中取出 request ,若沒有則會阻塞
            final Request<?> request = mCacheQueue.take();
            request.addMarker("cache-queue-take");

            // 如果該 request 被標記為取消,則跳過該 request ,不分發
            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                continue;
            }

            // 根據 request 的 url 去獲得緩存
            Cache.Entry entry = mCache.get(request.getCacheKey());
            if (entry == null) {
                request.addMarker("cache-miss");
                // 沒有緩存,把 Request 放入網絡請求隊列中 
                mNetworkQueue.put(request);
                continue;
            }

            // 若緩存失效,也放入網絡請求隊列中
            if (entry.isExpired()) {
                request.addMarker("cache-hit-expired");
                request.setCacheEntry(entry);
                mNetworkQueue.put(request);
                continue;
            }

            // 緩存存在,把緩存轉換為 Response
            request.addMarker("cache-hit");
            Response<?> response = request.parseNetworkResponse(
                    new NetworkResponse(entry.data, entry.responseHeaders));
            request.addMarker("cache-hit-parsed");
            // 判斷緩存是否需要刷新
            if (!entry.refreshNeeded()) {
                // 不需要刷新就直接讓 mDelivery 分發
                mDelivery.postResponse(request, response);
            } else {
                // 需要刷新緩存
                request.addMarker("cache-hit-refresh-needed");
                request.setCacheEntry(entry);

                // 先設置一個標志,表明該緩存可以先分發,之后需要重新刷新
                response.intermediate = true;

                // 利用 mDelivery 先把 response 分發下去,之后還要把該 request 加入到 mNetworkQueue 重新請求一遍
                mDelivery.postResponse(request, response, new Runnable() {
                    @Override
                    public void run() {
                        try {
                            mNetworkQueue.put(request);
                        } catch (InterruptedException e) {
                            // Not much we can do about this.
                        }
                    }
                });
            }

        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                return;
            }
            continue;
        }
    }
}

}</code></pre>

CacheDispatcher 類主要的代碼就如上面所示了,在主要的 run() 方法中都添加了注釋,閱讀起來應該比較簡單。那么在這里就貢獻一張 CacheDispatcher 類的流程圖:

CacheDispatcher 類的流程圖

NetworkDispatcher

然后是 NetworkDispatcher 的代碼:

public class NetworkDispatcher extends Thread {

...... // 省略部分源碼

public void quit() {
    mQuit = true;
    interrupt();
}

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void addTrafficStatsTag(Request<?> request) {
    // Tag the request (if API >= 14)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
    }
}

@Override
public void run() {
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    Request<?> request;
    while (true) {
        try {
            // 取出 request
            request = mQueue.take();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                return;
            }
            continue;
        }

        try {
            request.addMarker("network-queue-take");

            // 判斷是否被取消,和 CacheDispatcher 中的步驟一致
            if (request.isCanceled()) {
                request.finish("network-discard-cancelled");
                continue;
            }
            // 設置線程標識
            addTrafficStatsTag(request);

            // 處理網絡請求
            NetworkResponse networkResponse = mNetwork.performRequest(request);
            request.addMarker("network-http-complete");

            // 如果服務端返回 304 并且已經分發了一個響應,那么不再進行二次分發
            if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                request.finish("not-modified");
                continue;
            }

            // 得到 response
            Response<?> response = request.parseNetworkResponse(networkResponse);
            request.addMarker("network-parse-complete");

            // 如果該 request 需要進行緩存,那么保存緩存
            // TODO: Only update cache metadata instead of entire record for 304s.
            if (request.shouldCache() && response.cacheEntry != null) {
                mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
            }

            // 標記該 request 對應的 response 正在分發中
            request.markDelivered();
            // 分發該 request 對應的 response
            mDelivery.postResponse(request, response);
        } catch (VolleyError volleyError) {
            // 分發該 request 對應的 error
            parseAndDeliverNetworkError(request, volleyError);
        } catch (Exception e) {
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
            // 分發該 request 對應的 error
            mDelivery.postError(request, new VolleyError(e));
        }
    }
}

private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
    error = request.parseNetworkError(error);
    mDelivery.postError(request, error);
}

}</code></pre>

同樣的,根據 NetworkDispatcher 我們也可以梳理出一張流程圖:

NetworkDispatcher 類的流程圖

Request

到這里,我們把目光轉向 Request 。Request 是一個抽象類:

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

...// 省略絕大部分源碼-_- !

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

abstract protected void deliverResponse(T response);

protected VolleyError parseNetworkError(VolleyError volleyError) {
    return volleyError;
}

public void deliverError(VolleyError error) {
    if (mErrorListener != null) {
        mErrorListener.onErrorResponse(error);
    }
}

// request 完成響應分發后調用 
void finish(final String tag) {
    if (mRequestQueue != null) {
        // 跳轉到 RequestQueue.finish 方法
        mRequestQueue.finish(this);
    }
    if (MarkerLog.ENABLED) {
        final long threadId = Thread.currentThread().getId();
        if (Looper.myLooper() != Looper.getMainLooper()) {
            // If we finish marking off of the main thread, we need to
            // actually do it on the main thread to ensure correct ordering.
            Handler mainThread = new Handler(Looper.getMainLooper());
            mainThread.post(new Runnable() {
                @Override
                public void run() {
                    mEventLog.add(tag, threadId);
                    mEventLog.finish(this.toString());
                }
            });
            return;
        }

        mEventLog.add(tag, threadId);
        mEventLog.finish(this.toString());
    } else {
        long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
        if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
            VolleyLog.d("%d ms: %s", requestTime, this.toString());
        }
    }
}

@Override
public int compareTo(Request<T> other) {
    Priority left = this.getPriority();
    Priority right = other.getPriority();

    // request 優先級高的比低的排在隊列前面,優先被請求
    // 如果優先級一樣,按照 FIFO 的原則排列
    return left == right ?
            this.mSequence - other.mSequence :
            right.ordinal() - left.ordinal();
}

}</code></pre>

Request 實現了 Comparable 接口,這是因為 Request 是有優先級的,優先級高比優先級低的要先響應,排列在前。默認有四種優先級:

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

另外,子類繼承 Request 還要實現兩個抽象方法:

  • parseNetworkResponse :把 NetworkResponse 轉換為合適類型的 Response;
  • deliverResponse :把解析出來的類型分發給監聽器回調。

另外,Request 還支持八種請求方式:

/**

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

    在 Volley 中,Request 的子類眾多,有 StringRequest 、JsonObjectRequest(繼承自 JsonRequest ) 、JsonArrayRequest(繼承自 JsonRequest ) 和 ImageRequest 等。當然這些子類并不能滿足全部的場景要求,而這就需要我們開發者自己動手去擴展了。

    下面我就分析一下 StringRequest 的源碼,其他子類的源碼都是類似的,可以回去自行研究。

    public class StringRequest extends Request<String> {

    private final Listener<String> mListener;

    public StringRequest(int method, String url, Listener<String> listener,

         ErrorListener errorListener) {
     super(method, url, errorListener);
     mListener = listener;
    

    }

    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {

     this(Method.GET, url, listener, errorListener);
    

    }

    @Override protected void deliverResponse(String response) {

     // 得到相應的 response 后,回調 Listener 的接口
     mListener.onResponse(response);
    

    }

    @Override protected Response<String> parseNetworkResponse(NetworkResponse response) {

     String parsed;
     try {
         // 把字節數組轉化為字符串
         parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));(response.headers));
     } catch (UnsupportedEncodingException e) {
         parsed = new String(response.data);
     }
     return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    

    }

}</code></pre>

我們發現 StringRequest 的源碼十分簡潔。在 parseNetworkResponse 方法中主要把 response 中的 data 轉化為對應的 String 類型。然后回調 Response.success 即可。

看完了 Request 之后,我們來分析一下 Network 。

Network

Network 是一個接口,里面就一個方法 performRequest(Request<?> request) :

public interface Network {

public NetworkResponse performRequest(Request<?> request) throws VolleyError;

}</code></pre>

光看這個方法的定義就知道這個方法是用來干什么了!就是根據傳入的 Request 執行,轉換為對應的 NetworkResponse 的,并且該 NetworkResponse 不為空。我們就跳到它的實現類中看看該方法具體是怎么樣的。

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

private static int SLOW_REQUEST_THRESHOLD_MS = 3000;

private static int DEFAULT_POOL_SIZE = 4096;

protected final HttpStack mHttpStack;

protected final ByteArrayPool mPool;

public BasicNetwork(HttpStack httpStack) {
    // 使用 ByteArrayPool 可以實現復用,節約內存
    this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}

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<String, String> responseHeaders = new HashMap<String, String>();
        try {
            // 得到請求頭
            Map<String, String> headers = new HashMap<String, String>();
            // 添加緩存頭部信息
            addCacheHeaders(headers, request.getCacheEntry());
            httpResponse = mHttpStack.performRequest(request, headers);
            // 得到響應行
            StatusLine statusLine = httpResponse.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            // 轉化得到響應頭
            responseHeaders = convertHeaders(httpResponse.getAllHeaders());
            // 如果返回的狀態碼是304(即:HttpStatus.SC_NOT_MODIFIED)
            // 那么說明服務端的數據沒有變化,就直接從之前的緩存中取
            if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                        request.getCacheEntry() == null ? null : request.getCacheEntry().data,
                        responseHeaders, true);
            }

            // Some responses such as 204s do not have content.  We must check.
            // 有一些響應可能沒有內容,比如,所以要判斷一下
            if (httpResponse.getEntity() != null) {
                // 把 entiity 轉為 byte[]
              responseContents = entityToBytes(httpResponse.getEntity());
            } else {
              // Add 0 byte response as a way of honestly representing a
              // no-content request.
              responseContents = new byte[0];
            }

            // 如果該請求的響應時間超過 SLOW_REQUEST_THRESHOLD_MS(即 3000ms) ,會打印相應的日志
            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);
        } 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);
            }
            VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
            if (responseContents != null) {
                networkResponse = new NetworkResponse(statusCode, responseContents,
                        responseHeaders, false);
                if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                        statusCode == HttpStatus.SC_FORBIDDEN) {
                    // 啟動重試策略, 認證錯誤
                    attemptRetryOnException("auth",
                            request, new AuthFailureError(networkResponse));
                } else {
                    // TODO: Only throw ServerError for 5xx status codes.
                    throw new ServerError(networkResponse);
                }
            } else {
                throw new NetworkError(networkResponse);
            }
        }
    }
}

/**
 * 如果請求的響應時間超過 SLOW_REQUEST_THRESHOLD_MS ,就打印相應的日志
 */
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());
    }
}

/**
 * 進行重試策略,如果不滿足重試的條件會拋出異常
 */
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));
}

// 在請求行中添加緩存相關
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
    // If there's no cache entry, we're done.
    if (entry == null) {
        return;
    }

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

    if (entry.serverDate > 0) {
        Date refTime = new Date(entry.serverDate);
        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<String, String>.
 */
private static Map<String, String> convertHeaders(Header[] headers) {
    Map<String, String> result = new HashMap<String, String>();
    for (int i = 0; i < headers.length; i++) {
        result.put(headers[i].getName(), headers[i].getValue());
    }
    return result;
}

}</code></pre>

我們把 BasicNetwork 的源碼全部看下來,發現 BasicNetwork 干的事情就如下:

  • 利用 HttpStack 執行請求,把響應 HttpResponse 封裝為 NetworkResponse ;
  • 如果在這過程中出錯,會有重試策略。

至于 NetworkResponse 的源碼在這里就不分析了,主要是一個相對于 HttpResponse 的封裝類,可以自己去看其源碼。

得到 NetworkResponse 之后,在 NetworkDispatcher 中經過 Request 的 parseNetworkResponse 方法把 NetworkResponse 轉化為了 Response 。(具體可參考上面分析的 NetworkDispatcher 和 StringRequest 源碼)

那么接下來就把目光轉向 Response 吧。

Response

Response 類的源碼比較簡單,一起來看看:

public class Response<T> {

/** 分發響應結果的接口 */
public interface Listener<T> {
    /** Called when a response is received. */
    public void onResponse(T response);
}

/** 分發響應錯誤的接口 */
public interface ErrorListener {
    /**
     * Callback method that an error has been occurred with the
     * provided error code and optional user-readable message.
     */
    public void onErrorResponse(VolleyError error);
}

/** 通過這個靜態方法構造 Response */
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
    return new Response<T>(result, cacheEntry);
}

/**
 * 通過這個靜態方法構造錯誤的 Response
 */
public static <T> Response<T> error(VolleyError error) {
    return new Response<T>(error);
}

/** Parsed response, or null in the case of error. */
public final T result;

/** Cache metadata for this response, or null in the case of error. */
public final Cache.Entry cacheEntry;

/** Detailed error information if <code>errorCode != OK</code>. */
public final VolleyError error;

/** True if this response was a soft-expired one and a second one MAY be coming. */
public boolean intermediate = false;

/**
 * 是否成功
 */
public boolean isSuccess() {
    return error == null;
}


private Response(T result, Cache.Entry cacheEntry) {
    this.result = result;
    this.cacheEntry = cacheEntry;
    this.error = null;
}

private Response(VolleyError error) {
    this.result = null;
    this.cacheEntry = null;
    this.error = error;
}

}</code></pre>

Response 類主要通過 success 和 error 兩個方法分別來構造正確的響應結果和錯誤的響應結果。另外,在 Response 類中還有 Listener 和 ErrorListener 兩個接口。在最終的回調中會使用到它們。

在得到了 Response 之后,就要使用 ResponseDelivery 來分發了。那下面就輪到 ResponseDelivery 了,go on !!

ResponseDelivery

public interface ResponseDelivery {
    /**

 * 分發該 request 的 response
 */
public void postResponse(Request<?> request, Response<?> response);

/**
 * 分發該 request 的response ,runnable 會在分發之后執行
 */
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);

/**
 * 分發該 request 錯誤的 response
 */
public void postError(Request<?> request, VolleyError error);

}</code></pre>

ResponseDelivery 的接口就定義了三個方法,我們需要在其實現類中看看具體的實現:

public class ExecutorDelivery implements ResponseDelivery {
    /* 用來分發 Response , 一般都是在主線程中/
    private final Executor mResponsePoster;

/**
 * 傳入的 Handler 為主線程的
 */
public ExecutorDelivery(final Handler handler) {
    // Make an Executor that just wraps the handler.
    mResponsePoster = new Executor() {
        @Override
        public void execute(Runnable command) {
            handler.post(command);
        }
    };
}

/**
 * Creates a new response delivery interface, mockable version
 * for testing.
 * @param executor For running delivery tasks
 */
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));
}

/**
 * A Runnable 用來分發 response 到主線程的回調接口中
 */
@SuppressWarnings("rawtypes")
private class ResponseDeliveryRunnable implements Runnable {
    private final Request mRequest;
    private final Response mResponse;
    private final Runnable mRunnable;

    public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
        mRequest = request;
        mResponse = response;
        mRunnable = runnable;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        // 如果 request 被標記為取消,那么不用分發
        if (mRequest.isCanceled()) {
            mRequest.finish("canceled-at-delivery");
            return;
        }

        // 根據 mResponse 是否成功來分發到不同的方法
        if (mResponse.isSuccess()) {
            mRequest.deliverResponse(mResponse.result);
        } else {
            mRequest.deliverError(mResponse.error);
        }

        // If this is an intermediate response, add a marker, otherwise we're done
        // and the request can be finished. 
        if (mResponse.intermediate) {
            mRequest.addMarker("intermediate-response");
        } else {
            mRequest.finish("done");
        }

        // 運行 mRunnable
        if (mRunnable != null) {
            mRunnable.run();
        }
   }
}

}</code></pre>

ResponseDelivery 將根據 mResponse 是否成功來調用不同的方法 mRequest.deliverResponse 和 mRequest.deliverError 。在 mRequest.deliverResponse 中會回調 Listener 的 onResponse 方法;而在 mRequest.deliverError 中會回調 ErrorListener 的 onErrorResponse 方法。至此,一個完整的網絡請求及響應流程走完了。

HttpStack

現在回過頭來看看 Volley 框架中是如何發起網絡請求的。在本文的開頭中說過,Volley 是會根據 Android 的版本來選擇對應的 HttpStack。那么下面我們來深入看一下 HttpStack 的源碼。

public interface HttpStack {
    /**

 * 通過所給的參數執行請求
 */
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
    throws IOException, AuthFailureError;

}</code></pre>

HttpStack 接口中定義的方法就只有一個。我們要分別來看看 HurlStack 和 HttpClientStack 各自的實現。

HurlStack :

@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
        throws IOException, AuthFailureError {
    String url = request.getUrl();
    HashMap<String, String> map = new HashMap<String, String>();
    // 把請求頭放入 map 中
    map.putAll(request.getHeaders());
    map.putAll(additionalHeaders);
    if (mUrlRewriter != null) {
        String rewritten = mUrlRewriter.rewriteUrl(url);
        if (rewritten == null) {
            throw new IOException("URL blocked by rewriter: " + url);
        }
        url = rewritten;
    }
    URL parsedUrl = new URL(url);
    // 使用 HttpURLConnection 來發起請求
    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 is returned by getResponseCode() if the response code could not be retrieved.
        // Signal to the caller that something was wrong with the connection.
        throw new IOException("Could not retrieve response code from HttpUrlConnection.");
    }
    // 把響應封裝進 response 中
    StatusLine responseStatus = new BasicStatusLine(protocolVersion,
            connection.getResponseCode(), connection.getResponseMessage());
    BasicHttpResponse response = new BasicHttpResponse(responseStatus);
    response.setEntity(entityFromConnection(connection));
    for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
        if (header.getKey() != null) {
            Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
            response.addHeader(h);
        }
    }
    return response;
}

HttpClientStack :

@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
        throws IOException, AuthFailureError {
    // 根據請求方法生成對應的 HttpUriRequest
    HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
    // 添加請求頭
    addHeaders(httpRequest, additionalHeaders);
    addHeaders(httpRequest, request.getHeaders());
    onPrepareRequest(httpRequest);
    HttpParams httpParams = httpRequest.getParams();
    int timeoutMs = request.getTimeoutMs();
    // TODO: Reevaluate this connection timeout based on more wide-scale
    // data collection and possibly different for wifi vs. 3G.
    HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
    HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
    return mClient.execute(httpRequest);
}

在這里只給出 HurlStack 和 HttpClientStack 的 performRequest 方法。我們可以看到 HurlStack 和 HttpClientStack 已經把 HttpUrlConnection 和 HttpClient 封裝得很徹底了,以后哪里有需要的地方可以直接使用。

RetryPolicy

RetryPolicy 接口主要的作用就是定制重試策略,我們從下面的源碼可以看出該接口有三個抽象方法:

  • getCurrentTimeout :得到當前超時時間;
  • getCurrentRetryCount :得到當前重試的次數;
  • retry :是否進行重試,其中的 error 參數為異常的信息。若在 retry 方法中跑出 error 異常,那 Volley 就會停止重試。
public interface RetryPolicy {

/**
 * Returns the current timeout (used for logging).
 */
public int getCurrentTimeout();

/**
 * Returns the current retry count (used for logging).
 */
public int getCurrentRetryCount();

/**
 * Prepares for the next retry by applying a backoff to the timeout.
 * @param error The error code of the last attempt.
 * @throws VolleyError In the event that the retry could not be performed (for example if we
 * ran out of attempts), the passed in error is thrown.
 */
public void retry(VolleyError error) throws VolleyError;

}</code></pre>

RetryPolicy 接口有一個默認的實現類 DefaultRetryPolicy ,DefaultRetryPolicy 的構造方法有兩個:

/* The default socket timeout in milliseconds /
public static final int DEFAULT_TIMEOUT_MS = 2500;

/* The default number of retries / public static final int DEFAULT_MAX_RETRIES = 1;

/* The default backoff multiplier / public static final float DEFAULT_BACKOFF_MULT = 1f;

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

/**

  • Constructs a new retry policy.
  • @param initialTimeoutMs The initial timeout for the policy.
  • @param maxNumRetries The maximum number of retries.
  • @param backoffMultiplier Backoff multiplier for the policy. / public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) { mCurrentTimeoutMs = initialTimeoutMs; mMaxNumRetries = maxNumRetries; mBackoffMultiplier = backoffMultiplier; }</code></pre>

    從上面可以看到,在 Volley 內部已經有一套默認的參數配置了。當然,你也可以通過自定義的形式來設置重試策略。

    @Override
    public void retry(VolleyError error) throws VolleyError {
     // 重試次數自增
     mCurrentRetryCount++;
     // 超時時間自增,mBackoffMultiplier 為超時時間的因子
     mCurrentTimeoutMs += (mCurrentTimeoutMs  mBackoffMultiplier);
     // 如果超過最大次數,拋出異常
     if (!hasAttemptRemaining()) {
    
     throw error;
    
    } }

/**

  • Returns true if this policy has attempts remaining, false otherwise. */ protected boolean hasAttemptRemaining() { return mCurrentRetryCount <= mMaxNumRetries; }</code></pre>

    Cache

    分析完了前面這么多的類,終于輪到了最后的 Cache 。Cache 接口中定義了一個內部類 Entry ,還有定義了幾個方法:

    • get(String key) :根據傳入的 key 來獲取 entry ;
    • put(String key, Entry entry) :增加或者替換緩存;
    • initialize() :初始化,是耗時的操作,在子線程中調用;
    • invalidate(String key, boolean fullExpire) :根據 key 使之對應的緩存失效;
    • remove(String key) :根據 key 移除某個緩存;
    • clear() :清空緩存。
    public interface Cache {
     /**

    • Retrieves an entry from the cache.
    • @param key Cache key
    • @return An {@link Entry} or null in the event of a cache miss */ public Entry get(String key);

      /**

    • Adds or replaces an entry to the cache.
    • @param key Cache key
    • @param entry Data to store and metadata for cache coherency, TTL, etc. */ public void put(String key, Entry entry);

      /**

    • Performs any potentially long-running actions needed to initialize the cache;
    • will be called from a worker thread. */ public void initialize();

      /**

    • Invalidates an entry in the cache.
    • @param key Cache key
    • @param fullExpire True to fully expire the entry, false to soft expire */ public void invalidate(String key, boolean fullExpire);

      /**

    • Removes an entry from the cache.
    • @param key Cache key */ public void remove(String key);

      /**

    • Empties the cache. */ public void clear();

}</code></pre>

內部類 Entry ,Entry 中有一個屬性為 etag ,上面的源碼中也有 etag 的身影。

public static class Entry {
    /* 緩存中數據,即響應中的 body /
    public byte[] data;

/** HTTP頭部的一個定義,允許客戶端進行緩存協商 */
public String etag;

/** 服務端響應的時間 */
public long serverDate;

/** 緩存過期的時間,若小于當前時間則過期 */
public long ttl;

/** 緩存的有效時間,若小于當前時間則可以進行刷新操作 */
public long softTtl;

/** 響應的頭部信息 */
public Map<String, String> responseHeaders = Collections.emptyMap();

/** 判斷緩存是否有效,若返回 true 則緩存失效. */
public boolean isExpired() {
    return this.ttl < System.currentTimeMillis();
}

/** 判斷緩存是否需要刷新 */
public boolean refreshNeeded() {
    return this.softTtl < System.currentTimeMillis();
}

}</code></pre>

看完了 Cache 接口之后,我們來看一下實現類 DiskBasedCache 。首先是它的構造方法:

private static final int DEFAULT_DISK_USAGE_BYTES = 5  1024  1024;

public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { mRootDirectory = rootDirectory; mMaxCacheSizeInBytes = maxCacheSizeInBytes; }

public DiskBasedCache(File rootDirectory) { this(rootDirectory, DEFAULT_DISK_USAGE_BYTES); }</code></pre>

從構造方法中傳入的參數可知,Volley 默認最大磁盤緩存為 5M 。

DiskBasedCache 的 get(String key) 方法:

@Override
public synchronized Entry get(String key) {
    // 得到對應的緩存摘要信息
    CacheHeader entry = mEntries.get(key);
    // if the entry does not exist, return.
    if (entry == null) {
        return null;
    }
    // 得到緩存文件
    File file = getFileForKey(key);
    // CountingInputStream 為自定義的 IO 流,繼承自 FilterInputStream
    // 具有記憶已讀取的字節數的功能
    CountingInputStream cis = null;
    try {
        cis = new CountingInputStream(new FileInputStream(file));
        CacheHeader.readHeader(cis); // eat header
        // 得到緩存中的數據 data[] 
        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;
    } finally {
        if (cis != null) {
            try {
                cis.close();
            } catch (IOException ioe) {
                return null;
            }
        }
    }
}

// 把 url 分成兩半,分別得到對應的 hashcode ,拼接后得到對應的文件名 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; }

/**

  • Returns a file object for the given cache key. */ public File getFileForKey(String key) { return new File(mRootDirectory, getFilenameForKey(key)); }</code></pre>

    DiskBasedCache 的 putEntry(String key, CacheHeader entry) 方法:

    @Override
    public synchronized void put(String key, Entry entry) {
     // 檢查磁盤空間是否足夠,若不夠會刪除一些緩存文件
     pruneIfNeeded(entry.data.length);
     File file = getFileForKey(key);
     try {
    
     FileOutputStream fos = new FileOutputStream(file);
     CacheHeader e = new CacheHeader(key, entry);
     e.writeHeader(fos);
     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());
    
    } }

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); }</code></pre>

initialize() 方法:

@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) {
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(file);
        // 得到緩存文件的摘要信息
        CacheHeader entry = CacheHeader.readHeader(fis);
        entry.size = file.length();
        // 放入 map 中
        putEntry(entry.key, entry);
    } catch (IOException e) {
        if (file != null) {
           file.delete();
        }
    } finally {
        try {
            if (fis != null) {
                fis.close();
            }
        } catch (IOException ignored) { }
    }
}

}</code></pre>

invalidate(String key, boolean fullExpire) 方法:

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

}</code></pre>

remove(String key) 和 clear() 方法比較簡單,就不需要注釋了:

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

@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."); }</code></pre>

0100B

至此,Volley 源碼解析差不多已經結束了。基本上在整個 Volley 框架中至關重要的類都講到了。當然,還有一些 NetworkImageView 、ImageLoader 等源碼還沒解析。由于本篇文章內容太長了(有史以來寫過最長的一篇─=≡Σ((( つ??ω??)つ),只能等到下次有機會再給大家補上了。

在這還給出了一張整個 Volley 框架大致的網絡通信流程圖,對上面源碼沒看懂的童鞋可以參考這張圖再看一遍:

Volley框架大致流程圖

最后,只剩下總結了。從頭到尾分析了一遍,發現 Volley 真的是一款很優秀的框架,面向接口編程在其中發揮到極致。其中有不少值得我們借鑒的地方,但是 Volley 并不是沒有缺點的,對于大文件傳輸 Volley 就很不擅長,搞不好會 OOM 。另外,在源碼中還有不少可以繼續優化的地方,有興趣的同學可以自定義一個屬于自己的 Volley 。

好了,如果你對本文哪里有問題或者不懂的地方,歡迎留言一起交流。

References

 

來自:http://www.jianshu.com/p/22e9c5a45cfd

 

 本文由用戶 LowMerlin 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!