Volley框架原理

Volley有以下優勢
1. 自動調度網絡請求
2. 多併發請求 (源於開了多個線程)
3. 本地Cache自動緩存網絡請求結果
4. 支持設置請求優先級
5. 支持取消單個請求或者取消全部請求
6. 易於定製請求(好比:自定義重試機制,自定義Request請求等)
7. 提供完善的Log打印跟蹤工具

數組

Google的一張Volley原理圖來簡單解釋下Volley的工做原理。 緩存

Volley請求處理是一個異步的過程:網絡

1.在主線程中按照請求的優先級把Request添加到本地緩存隊列CacheQueue中,
2.緩存分發器CacheDispatcher輪詢本地是否已經緩存了此次請求的結果
3.若是命中,則從緩存中讀取數據而且解析。解析完的結果被ResponseDelivery 分發到主線程中。
4.若是沒有命中,則將此次請求添加到網絡請求隊列NetworkQueue中,
5.網絡分發器NetworkDispatcher處理網絡請求,獲取請求結果並解析同時把結果寫入緩存。解析完的結果被分發到主線程中。多線程

  • 主線程:全部的請求結果都會被分發到主線程。
  • 緩存線程:專門有一個線程用於讀取本地緩存。
  • 網絡線程:Volley默認開啓4個線程去處理網絡請求。
  •  

 兩個分發器、兩個隊列、五個線程併發

Volley初始化之後就建立了5個後臺線程(1個緩存線程和4個網絡線程來處理Request請求)在處理請求。只要你沒作處理,這5個線程一直在後臺跑。爲了節省資源,在同一個App中最好使用同一個單例Volley RequestQueue隊列來處理全部請求,以避免建立過多線程浪費資源。還有在退出這個應用時,應該調用 RequestQueue#stop方法來幹掉全部Volley線程。如此纔是使用Volley最優雅的方式app

從源碼角度理解Volley工做原理

Volley最基本的使用代碼以下(開發者用法):框架

//建立請求隊列
 RequestQueue mQueue = Volley.newRequestQueue(context);
 //構建一個Request請求
 StringRequest request = new StringRequest(url, new Response.Listener<String>() {
     @Override
     public void onResponse(String response) {
     }
 }, new Response.ErrorListener() {
     @Override
     public void onErrorResponse(VolleyError error) {
     }
 });
 //將請求添加到隊列中
 mQueue.add(request);

咱們來看看Volley#newRequestQueue()方法如何實現的?異步

Volley類

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

        String userAgent = "volley/0";
        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException e) {
        }

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {//API>=9使用HttpURLConnection訪問網絡
                stack = new HurlStack();
            } else {//API<9時使用HttpClient訪問網絡
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        //建立網絡訪問類
        Network network = new BasicNetwork(stack);
        //建立請求隊列
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        //開始執行隊列中的任務
        queue.start();
        return queue;
    }

    /**靜態方法建立請求隊列*/
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }
}
--------------------- 

代碼第8行: 建立一個默認的緩存文件目錄,該路徑在應用的私有目錄data/data/your_package/cache/volley/ 下。ide

代碼28行:建立一個網絡請求隊列RequestQueue 對象,而後調用start()方法啓動執行隊列中的任務。那麼RequestQueue#start()方法到底作了什麼?接下來分析下RequestQueue類的實現。工具

RequestQueue

public class RequestQueue {


    /** 用於標識Request的編號. */
    private AtomicInteger mSequenceGenerator = new AtomicInteger();

    /**保存添加到RequestQueue隊列中相同key的請求*/
    private final Map<String, Queue<Request<?>>> mWaitingRequests =
            new HashMap<String, Queue<Request<?>>>();

    /**保存當前全部添加到RequestQueue隊列中的請求*/
    private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();

    /** 帶有優先級的緩存請求隊列. */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<Request<?>>();

    /** 帶有優先級的網絡請求隊列. */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<Request<?>>();

    /** 默認開啓4個線程處理網絡請求 */
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    /** 本地緩存,用於保存網絡請求結果 */
    private final Cache mCache;

    /** 用於執行網絡請求. */
    private final Network mNetwork;

    /** 請求結果分發器. */
    private final ResponseDelivery mDelivery;

    /** 網絡處理請求分發器. */
    private NetworkDispatcher[] mDispatchers;

    /** 本地緩存處理請求分發器. */
    private CacheDispatcher mCacheDispatcher;

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

    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 void start() {
        stop();  //中止當前全部任務.
        // 建立緩存任務調度器,而且啓動它
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // 建立默認個數的網絡任務調度器,而且啓動它.
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

    /**
     * 中止緩存和網絡調度
     */
    public void stop() {
        if (mCacheDispatcher != null) {
            mCacheDispatcher.quit();
        }
        for (int i = 0; i < mDispatchers.length; i++) {
            if (mDispatchers[i] != null) {
                mDispatchers[i].quit();
            }
        }
    }

    /**
     * 獲取隊列編號.
     */
    public int getSequenceNumber() {
        return mSequenceGenerator.incrementAndGet();
    }

    /**
     * 獲取本地緩存.
     */
    public Cache getCache() {
        return mCache;
    }

    /**
     完成一次請求,當該請求被執行結束或者該請求被取消時調用該方法
     */
    <T> void finish(Request<T> request) {
        // 從當前隊列中移除該請求,標誌着該請求獲得執行。
        synchronized (mCurrentRequests) {
            mCurrentRequests.remove(request);
        }
        synchronized (mFinishedListeners) {
          for (RequestFinishedListener<T> listener : mFinishedListeners) {
            listener.onRequestFinished(request);
          }
        }
        //若是該請求容許有緩存,則將等待隊列中的全部的請求任務所有添加到緩存隊列中繼續執行。
        if (request.shouldCache()) {
            synchronized (mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
                if (waitingRequests != null) {
                    if (VolleyLog.DEBUG) {
                        VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
                                waitingRequests.size(), cacheKey);
                    }
                    //處理等待隊列中全部的請求.
                    mCacheQueue.addAll(waitingRequests);
                }
            }
        }
    }
--------------------- 

RequestQueue#start方法
有上面的代碼可知:start方法中建立了一個CacheDispatcher緩存調度處理器和4個NetworkDispatcher網絡調度處理器,而他們都是繼承自Thread線程的,因此這裏建立了1個緩存線程和4個網絡線程來處理Request請求。至關於此處啓動了5個線程來處理請求,這就是爲何Volley框架支持多併發請求了。那麼咱們看看它們都作了些什麼??
--------------------- ------------------------------------------------------------ 

CacheDispatcher類(緩存分發器)

public class CacheDispatcher extends Thread {
    /** 緩存阻塞隊列. */
    private final BlockingQueue<Request<?>> mCacheQueue;

    /** 網絡阻塞隊列. */
    private final BlockingQueue<Request<?>> mNetworkQueue;

    /** 本地緩存. */
    private final Cache mCache;

    /** 結果分發器. */
    private final ResponseDelivery mDelivery;

    /** 標記當前線程是否死亡. */
    private volatile boolean mQuit = false;

    public CacheDispatcher(
            BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }

    /**退出當前線程*/
    public void quit() {
        mQuit = true;
        interrupt();
    }

    @Override
    public void run() {
//設置該線程的優先級爲後臺線程                           Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        //初始化本地緩存.
        mCache.initialize();
        //死循環
        while (true) {
            try {
                //從阻塞的緩存隊列中取出一個請求.
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                // 若是該請求被取消,就不處理該請求
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;//繼續下次循環,等待下一個請求
                }

                //試圖從本地緩存中讀取本次請求結果.
                Cache.Entry entry = mCache.get(request.getCacheKey());
                //本地沒有命中則將該請求投放到網絡請求隊列中處理
                if (entry == null) {
                    request.addMarker("cache-miss");
                    mNetworkQueue.put(request);
                    continue;//結束本次循環
                }

                // 本地命中,可是過時了,也需再次將請求投放到網絡請求隊列中
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;//結束本次循環
                }

                // 本地緩存命中該請求之後解析該請求
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    // 本地緩存命中且沒有過時,則將解析的結果發送到主線程中.
                    mDelivery.postResponse(request, response);
                } else {
                    // 本地緩存命中,但須要刷新,從新將此次請求投放到網絡請求隊列中
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;
                    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) {
                // 此處用於退出當前線程,當該線程發生中斷異常時執行.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }
}
--------------------- 

解析:CacheDispatcher類繼承自Thread(緩存線程),實現了run方法,在run方法中寫了一個while(true)死循環,用於一直讀取緩存隊列中的請求任務(輪詢)。由於緩存隊列mCacheQueue是一個阻塞隊列,因此只有隊列不爲空時while循環纔會取出下一個新的請求任務執行,不然while循環一直阻塞直到有新任務添加進來。

run方法實現的邏輯是:先從本地緩存中去讀本次請求,若是該請求命中本地緩存且緩存未過時,則解析結果而且由分發器ResponseDelivery 將結果發送到主線程中。若是本地沒有命中或者命中的請求過時了,則將該請求投放到網絡請求隊列中,由NetworkDispatcher來處理網絡請求。

--------------------- ----- ------------------------------------------------------------ 

NetworkDispatcher類(網絡分發器)

public class NetworkDispatcher extends Thread {

..................
    @Override
    public void run() {
    //設置線程優先級爲後天線程
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                // 從隊列中取出一個請求任務.
                request = mQueue.take();
            } catch (InterruptedException e) {
                // 當線程發生中斷異常時,判斷該線程是否死亡?若是死亡則結束循環,不然跳出本次循環繼續等待下一個請求任務到來。
                if (mQuit) {
                    return;
                }
                continue;
            }

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

                // 該Request請求若是被取消,則跳出本次循環,結束本地請求處理
                // network request.
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

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

                // 相同的結果不發送第二次
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // 在工做線程中解析網絡結果.
                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);
            } catch (VolleyError volleyError) {
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                VolleyError volleyError = new VolleyError(e);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                //將錯誤結果發送到主線程中
                mDelivery.postError(request, volleyError);
            }
        }
    }

  ...........
--------------------- 

解析:NetworkDispatcher 類一樣繼承自Thread,實現了其run方法。在該方法中寫了一個while(true)死循環,用於讀取網絡隊列中的Request請求任務,一樣因爲網絡隊列也是一個阻塞隊列,因此當隊列不爲空就取出一個Request任務,而後將該任務值執行網絡請求,而且解析請求結果,在獲得網絡請求結果之後首先將結果保存到本地緩存,然看結果將由分發器ResponseDelivery發送到主線程中。
--------------------- ---------------------------------------------- 

到此,RequestQueue#start方法分析結束,總結起來以下:Volley會建立一個RequestQueue對象,該對象會建立一個Cache對象用於保存請求結果,建立一個帶有優先級以及阻塞的緩存隊列mCacheQueue用於保存用戶添加的請求,建立一個CacheDispatcher線程調度器來輪詢緩存隊列mCacheQueue執行請求任務。建立了4個帶有優先級和阻塞的網絡隊列mNetWorkQueue用於保存沒有命中本地緩存的請求,匹配的也建立了4個NetworkDispatcher線程調度器來輪詢mNetWorkQueue隊列執行請求任務。
--------------------- -------------------- ---------------------------------------------- 

RequestQueue#add()

........

 /**添加一個請求到帶有分發器的隊列中*/
    public <T> Request<T> add(Request<T> request) {
        //Request請求和請求隊列關聯
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {
            //Request請求添加到當前請求隊列中
            mCurrentRequests.add(request);
        }

        // 給該請求設置一個順序編號.
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // 若是該請求不須要緩存,則將Request請求直接添加至網絡請求隊列中.
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }

        // 如下代碼處理緩存請求等待隊列.
        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();
            //若是等待隊列中已經存在該請求的key,則說明此時有一個相同的請求正在被處理,所以將該request放在等待隊列中,等待前一個request處理完以後在finish方法中處理。
            if (mWaitingRequests.containsKey(cacheKey)) {
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<Request<?>>();
                }
                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                //若是等待隊列中沒有改請求的key,則將之添加到等待隊列,注意:添加到等待隊列中的value 是一個null,目的用於標記該請求在等待隊列中且處於正在被處理的狀態。
                mWaitingRequests.put(cacheKey, null);
                //添加都緩存隊列中讓該請求獲得相應的執行。
                mCacheQueue.add(request);
            }
            return request;
        }
    }
........

代碼第13行:給當前請求設置一個編號,後面將用於設置請求的優先級,這裏暫且不詳細講解。
代碼第17-20行:判斷當前請求是否容許本地緩存,若是不容許,則直接將本次請求添加到網絡請求隊列中。不然添加到緩存請求隊列中。
代碼26-35行:判斷請求等待隊列中是否包含本次請求的key,若是包含,則說明有相同的請求正在被執行,此時將該請求放入到等待隊列中,等待上一個請求被執行完成之後再來處理這次的請求,見方法 finish()。
代碼38-39行:表示請求等待隊列中並不包含本次請求的key,則先將本次請求在等待隊列中置空,置空的目的是告訴別人該請求正在被執行,若是有其餘相同的請求來時,請先等待我此次請求執行結束。而後將本次請求投放到緩存隊列中,讓CacheDispatcher調度器執行本次請求。
---------------------

RequestQueue#finish完成一次請求時會調用該方法。

該方法的調用,標誌着一次請求的結束,結束一次請求包括:

  • 一個請求被完整的處理,獲得請求的結果。
  • 一個請求在處理的過程當中被取消了。
<T> void finish(Request<T> request) {
        // 從當前隊列中移除該請求
        synchronized (mCurrentRequests) {
            mCurrentRequests.remove(request);
        }
        synchronized (mFinishedListeners) {
        //請求結束的監聽回調
          for (RequestFinishedListener<T> listener : mFinishedListeners) {
            listener.onRequestFinished(request);
          }
        }

        if (request.shouldCache()) {
            synchronized (mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                //從等待隊列中移除該請求
                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
                if (waitingRequests != null) {
                   //將等待隊列中全部相同的請求一次性添加到緩存隊列中,讓CacheDispatcher線程處理。此處不敢苟同,既然是相同的請求,爲啥要所有放入隊列中?放一個就行了嘛!!
                    mCacheQueue.addAll(waitingRequests);
                }
            }
        }
    }

這段代碼的解釋在註釋裏寫的很清楚了。

取消請求

咱們都知道,Volley有個優點就是能夠取消指定的Tag標記請求或者取消全部請求,那麼Volley是怎麼作到的呢?仍是從源碼中找答案吧。

RequestQ ueue#cancleAll()

  /**
     *定義的過濾請求接口,用於構建取消某個請求或者全部請求
     */
    public interface RequestFilter {
        public boolean apply(Request<?> request);
    }

    /**
     根據給定的過濾條件取消隊列中全部的請求
     */
    public void cancelAll(RequestFilter filter) {
        synchronized (mCurrentRequests) {
        //遍歷當前請求隊列中全部的請求,找到匹配的請求而後取消該請求。
            for (Request<?> request : mCurrentRequests) {
                if (filter.apply(request)) {
                    request.cancel();
                }
            }
        }
    }

    /**
     根據給定的tag標籤取消隊列中全部的請求隊列
     */
    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) {
                return request.getTag() == tag;
            }
        });
    }

咱們在添加請求的時候一般會給每一個請求這是一個Tag,好比:

request.setTag("request1");

那麼咱們須要取消該請求的時候就簡單了:

RequestQueue.cancelAll("request1");

如此就取消了tag=request1的請求。
如此一來每一個請求都須要設置不一樣的tag來肯定惟一的請求標記。那麼問題來了,我在整個應用退出時該如何取消全部的請求呢?不可能我每一個請求都去調用一次cancleAll吧?此時只要調用cancelAll(RequestFilter filter)方法就能夠垂手可得的取消全部request請求啦,代碼以下:

requestQueue.cancelAll(new RequestQueue.RequestFilter() {
            @Override
            public boolean apply(Request<?> request) {
                return true;
            }
        });

解析:以上代碼僅僅是修改了RequestFilter接口中apply方法的返回值永遠爲true而已。如此一來就會致使以下方法會所有遍歷一次當前請求隊列。

 public void cancelAll(RequestFilter filter) {
        synchronized (mCurrentRequests) {
        //遍歷當前請求隊列中全部的請求,找到匹配的請求而後取消該請求。
            for (Request<?> request : mCurrentRequests) {
                if (filter.apply(request)) {
                    request.cancel();
                }
            }
        }
    }

總結

這篇博客主要介紹了Volley的總體工做機制,從整篇博客咱們知道:

Volley默認建立1個cache Thread和4個network Thread來處理網絡請求,固然你也能夠建立更多的network Thread來處理更多的網絡請求,正由於如此,Volley才支持多併發網絡鏈接。
Volley建立1個本地緩存隊列(cacheQueue)和1個網絡請求隊列(networkQueue)來保存全部網絡請求,而隨之對應的是一個cache Thread處理緩存隊列,4個network thread處理網絡請求隊列,因爲隊列的實現都是帶有優先級的阻塞隊列,所以4個network thread是自動調度處理網絡請求的。
Volley默認先將請求提交給cache Thread來處理,cache Thread會查找本地是否緩存了本次請求結果,若是緩存了且該結果未過時,則直接讀取本地緩存結果,而無須再次請求網絡。所以Volley默認自動緩存網絡請求結果。
Volley支持取消某個或者全部的網絡請求,通常在某個activity退出時調用Request#cancelAll()方法來取消全部網絡請求以便出現內存泄漏。
Volley初始化之後就建立了5個後臺線程在處理請求。只要你沒作處理,這5個線程一直在後臺跑。爲了節省資源,在同一個App中最好使用同一個單例Volley RequestQueue隊列來處理全部請求,以避免建立過多線程浪費資源。還有在退出這個應用時,應該調用 RequestQueue#stop方法來幹掉全部Volley線程。如此纔是使用Volley最優雅的方式
後續博客會繼續分析Volley是怎麼實現RetryPolicy錯誤重試機制的,以及本地緩存的策略和請求優先級的設置。
---------------------

volley爲何不適合傳輸大數據:

volley中爲了提升請求處理的速度,採用了ByteArrayPool進行內存中的數據存儲的,若是下載大量的數據,這個存儲空間就會溢出,因此不適合大量的數據,

可是因爲他的這個存儲空間是內存中分配的,當存儲的時候優是從ByteArrayPool中取出一塊已經分配的內存區域, 沒必要每次存數據都要進行內存分配,

而是先查找緩衝池中有無適合的內存區域,若是有,直接拿來用,從而減小內存分配的次數 ,因此他比較適合大量的數據量少的網絡數據交互狀況。

 Volley有用到線程池嗎?

volley雖然沒有用ThreadPoolExecutor,但volley 裏面使用了一個數組來存放 NetworkDispatcher 這功能就至關因而線程池,只不過本身寫了管理,默認開啓4個線程。

------------------------------------------------------------ 

相關文章
相關標籤/搜索