zoukankan      html  css  js  c++  java
  • 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最优雅的方式

    从源码角度理解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/ 下。

    代码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个线程。


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

    作者:废墟的树 
    来源:CSDN 
    原文:https://blog.csdn.net/feiduclear_up/article/details/52847017 
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    [Java][Android][Process] 分享 Process 运行命令行封装类型
    UVA 11992
    2014扬声器的信息中国建筑师大会
    POJ 1745 Divisibility (线性dp)
    ListView 实现多选/无线电
    UVa 11587
    zoj 2156
    [TroubleShooting] The server network address can not be reached or does not exist
    oracle,如何查看视图结构,获得视图中的字段名称、字段类型、字段长度等。
    实现文件上传,以及表单提交成功的回调函数
  • 原文地址:https://www.cnblogs.com/Jackie-zhang/p/9883330.html
Copyright © 2011-2022 走看看