美文网首页
Volley->01基本流程

Volley->01基本流程

作者: 冉桓彬 | 来源:发表于2018-04-26 09:26 被阅读4次

相关问题以及参考文章:

尝试从下面几个方面进行分析:

1. 关于线程管理;
2. 关于网络请求;
3. 关于数据缓存;

一、关于线程的管理:

1.1 Volley.newRequestQueue;
public class Volley {
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (BaseHttpStack) null);
    }

    public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
        BasicNetwork network;
        if (stack == null) {
            /**
             * 此时只考虑了sdk > 14的情况
             */
            network = new BasicNetwork(new HurlStack());
        } else {
            network = new BasicNetwork(stack);
        }
        return newRequestQueue(context, network);
    }
}
1.2 Volley.newRequestQueue:
public class Volley {
    private static RequestQueue newRequestQueue(Context context, Network network) {
        String path = "" + Environment.getExternalStorageDirectory();
        File cacheDir = new File(path, DEFAULT_CACHE_DIR);
        /**
         * 然后开始创建线程;
         */
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();
        return queue;
    }
}
1.3 RequestQueue:
public class RequestQueue {

    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;
        /**
         * 每次调用Volley.newRequestQueue时都会创建四个线程;
         */
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }
}

public class NetworkDispatcher extends Thread;
1.4 RequestQueue.start:
public class CacheDispatcher extends Thread;

public class RequestQueue {
    public void start() {
        /**
         * 停止当前正在运行的线程;
         */
        stop();  
        /**
         * 再次创建一个CacheDispatcher线程, 到目前为止已经创建了5个线程, 也就是说在
         * 初始化RequestQueue时, 会创建5个线程, 然后通过start让其一直处于运行状态;
         */
        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 (final NetworkDispatcher mDispatcher : mDispatchers) {
            if (mDispatcher != null) {
                mDispatcher.quit();
            }
        }
    }
}
1.5 CacheDispatcher.run:
public class CacheDispatcher extends Thread {

    private final BlockingQueue<Request<?>> mCacheQueue;

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        /**
         * 在运行缓存线程之前, 先进行磁盘缓存数据读取到内存中;模块<3.4>
         */
        mCache.initialize();

        while (true) {
            /**
             *  其实到这里可以推翻模块<1.4>里面的部分结论, Volley初始化时, 确实会创建5个线程, 
             *  但是这5个线程并不会一直处于运行状态, 当我们没有显示调用RequesQueue时, 这五个
             *  线程经过mCacheQueue.take()这个经典的生产者-消费者模式会被挂起, 直到有Request
             *  被添加至RequestQueue中, 所以如果处于空闲状态, 这5个线程并不会占用资源;
             */
            final Request<?> request = mCacheQueue.take();
            request.addMarker("cache-queue-take");

            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                continue;
            }

            // Attempt to retrieve this item from cache.
            Cache.Entry entry = mCache.get(request.getCacheKey());
            /**
             * 1. entry == null, 说明针对当前Request, 不存在缓存, 然后进入if内部;
             */
            if (entry == null) {
                request.addMarker("cache-miss");
                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                    /**
                     * 1. 结合模块<1.4>可知, CacheDispatcher和NetworkDispatcher持有同一个
                     *    mNetworkQueue;
                     * 2. 然后执行continue再次重复这次操作;
                     * 3. 由于CacheDispatcher与NetworkDispatcher持有同一个mNetworkQueue, 这里肯定是通过
                     *    mNetworkQueue去控制NetworkDispatcher里面的相关逻辑
                     */
                    mNetworkQueue.put(request);
                }
                continue;
            }                              
        }
    }
}
1.6 NetworkDispatcher.run:
public class NetworkDispatcher extends Thread {
    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            /**
             * 这里的mQueue就是模块<1.5>提及的mNetworkQueue, NetworkDispatcher线程被挂起/激活;
             */
            request = mQueue.take();
            ...
        }
    }
}
  • 1、模块一可知, 初始化Volley时, 会创建5个线程, 然后分别执行其run方法, 但是通过BlockingQueue的生产者-消费者模型, 控制线程的挂起与激活, 如果没有Request时, 5个线程会处于挂起状态, 直到有Request时, 才会被激活;
  • 2、CacheDispatcher与NetworkDispatcher共用同一个NetworkQueue, NetworkDispatcher的激活需要CacheDispatcher通过调用NetworkQueue.put去触发;
  • 3、CacheDispatcher的激活谁去触发呢?模块二关于请求会涉及到CacheDispatcher的激活条件;

二、关于网络请求:

2.1 RequestQueue.add:
public class RequestQueue {
    public <T> Request<T> add(Request<T> request) {
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");
        /**
         * 网络请求响应结果默认是否应当被缓存起来, 默认true, 表示应当被缓存, 既然response会被缓存,
         * 那么当请求到来时, 肯定优先将Request交给CacheDispatcher去处理;
         */
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }
        /**
         * 1. 结合模块<1.4> ~ <1.5>可知CacheDispatcher内部的mCacheQueue指向此处的mCacheQueue;
         * 2. 在模块<1.5>中, 如果mCacheQueue未空, CacheDispatcher会被挂起, 直到外界显示唤醒, 这里通过
         *    mCacheQueue.add的方式唤醒CacheDispatcher线程;
         */
        mCacheQueue.add(request);
        return request;
    }
}
public abstract class Request<T> implements Comparable<Request<T>> {
    /**
     * Whether or not responses to this request should be cached.
     */
    private boolean mShouldCache = true;
    /**
     * Returns true if responses to this request should be cached.
     */
    public final boolean shouldCache() {
        return mShouldCache;
    }
}
2.2 CacheDispatcher.run:
public class CacheDispatcher extends Thread {
    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        mCache.initialize();

        while (true) {
            final Request<?> request = mCacheQueue.take();
            request.addMarker("cache-queue-take");

            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                continue;
            }
//1--->
            /**
             * 1. 如果没有缓存, 即entry = null, 则进入if内部通过mNetworkQueue.put
             *    方式唤醒NetworkDispatcher, 模块<2.3>;
             * 2. 如果存在缓存, 进入模块<//2--->>;
             */
            Cache.Entry entry = mCache.get(request.getCacheKey());
            if (entry == null) {
                request.addMarker("cache-miss");
                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                    mNetworkQueue.put(request);
                }
                continue;
            }
//2--->
            /**  
             * 1. 如果缓存不为空, 在这里判断缓存是否过期, 如果缓存过期, 则将Request交给NetworkDispatcher进行处理;
             * 2. 缓存是否过期的依据?<TODO>
             */
            if (entry.isExpired()) {
                request.addMarker("cache-hit-expired");
                request.setCacheEntry(entry);
                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                    mNetworkQueue.put(request);
                }
                continue;
            }
            /**
             * 1. 如果缓存存在, 且服务器端缓存数据没有过期, 则从内存缓存中读取数据到Response中;
             * 2. 然后再次判断缓存数据是否需要进行刷新, 如果不需要刷新, 则直接在<//3--->>内部
             *    进行数据分发, 反之进入<//4--->>将Request交给NetworkDispatcher;
             */
            request.addMarker("cache-hit");
            Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
            request.addMarker("cache-hit-parsed");
//3--->
            if (!entry.refreshNeeded()) {
                mDelivery.postResponse(request, response);
            } else {
//4--->
                request.addMarker("cache-hit-refresh-needed");
                request.setCacheEntry(entry);
                response.intermediate = true;

                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                                mNetworkQueue.put(request);
                        }
                    });
                } else {
                    mDelivery.postResponse(request, response);
                }
            }
        }
    }
}
2.3 NetworkDispatcher.run:
public class NetworkDispatcher extends Thread {
    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
//1--->
            /**
             * 线程在模块<2.2>中被唤醒, 执行模块<//2--->>
             */
            request = mQueue.take();
            request.addMarker("network-queue-take");
            if (request.isCanceled()) {
                request.finish("network-discard-cancelled");
                request.notifyListenerResponseNotUsable();
                continue;
            }
//2--->
            /**
             * 进入到模块<2.4>中执行网络请求, 在进行Volley初始化时, 只考虑SDK>14的情况,
             * 此处mNetwork实际指向BasicNetwork, 其内部BaseHttpStack实际指向HurlStack;
             */                
            NetworkResponse networkResponse = mNetwork.performRequest(request);
            request.addMarker("network-http-complete");
            if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                request.finish("not-modified");
                request.notifyListenerResponseNotUsable();
                continue;
            }
            /**
             * 1. 将网络数据进行解析并封装进Response中;模块<3.5>, 然后通过<//3--->>完成数据内存, 磁盘缓存;
             * 2. parseNetworkResponse内部主要完成对响应头的解析与缓存, 然后等待下一次请求相同
             *    Request时, 可以通过响应头判断是否需要进行网络请求;
             */
            Response<?> response = request.parseNetworkResponse(networkResponse);
            request.addMarker("network-parse-complete");
            /**
             * 执行到这里判断request获取的结果是否需要进行缓存, shouldCache默认为true, 
             * 并且如果response有结果, 则对获取的结果进行缓存;模块<3.1>
             */
//3--->
            if (request.shouldCache() && response.cacheEntry != null) {
                mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
            }
            request.markDelivered();
            mDelivery.postResponse(request, response);
            request.notifyListenerResponseReceived(response);
        }
    }
}
2.4 BasicNetwork.performRequest:
public class BasicNetwork implements Network {
    @Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            List<Header> responseHeaders = Collections.emptyList();
            // Gather headers.
            Map<String, String> additionalRequestHeaders = getCacheHeaders(request.getCacheEntry());
            /**
             * 通过mBaseHttpStack最终将请求交给其内部的HttpURLConnection进行处理;
             */
            httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
            int statusCode = httpResponse.getStatusCode();
            responseHeaders = httpResponse.getHeaders();
            /**
             * 1. 根据不同的状态码做出不同的处理, https://www.jianshu.com/writer#/notebooks/11927320/notes/27327761
             *    对响应码做了简单的记录;
             * 2. 如果状态码为304, 则表示该Request已经请求过至少一次, 且服务器端数据没有发生变化,
             *    此时直接从缓存中取数据;
             * 3. 如果状态码不是304, 则跳转到模块<//2--->>读取流数据;
             */
//1--->
            if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
                Entry entry = request.getCacheEntry();
                if (entry == null) {
                    return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, null, true, SystemClock.elapsedRealtime() - requestStart, responseHeaders);
                }
                List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);
                return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, entry.data, true, SystemClock.elapsedRealtime() - requestStart, combinedHeaders);
            }
//2--->
            /**
             * 1. 将流数据写入到reponseContents中, 并封装进NetworkResponse中;
             * 2. 在模块<2.5>进行流数据的写入操作; 
             */
            InputStream inputStream = httpResponse.getContent();
            if (inputStream != null) {
                responseContents = inputStreamToBytes(inputStream, httpResponse.getContentLength());
            } else {
                responseContents = new byte[0];
            }
            // if the request is slow, log it.
            long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
            logSlowRequests(requestLifetime, request, responseContents, statusCode);
            if (statusCode < 200 || statusCode > 299) {
                throw new IOException();
            }
            return new NetworkResponse(statusCode, responseContents, false, SystemClock.elapsedRealtime() - requestStart, responseHeaders);
        }
    }
}
2.5 BasicNetwork.inputStreamToBytes:
public class BasicNetwork implements Network {
    private byte[] inputStreamToBytes(InputStream in, int contentLength) {
        PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(mPool, contentLength);
        byte[] buffer = null;
        try {
            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 {
            if (in != null) {
                in.close();
            }
            mPool.returnBuf(buffer);
            bytes.close();
        }
    }
}

三、数据缓存:

3.1 DiskBasedCache.put:
public class DiskBasedCache implements Cache {
    @Override
    public synchronized void put(String key, Entry entry) {
        /**
         * 1. 在对数据进行缓存之前, 需要先判断缓存数据是否已满, 如果满了, 需要先对缓存数据进行处理,
         *    这里的处理是采用LRU方式, 对最近最少使用的数据进行删除;
         * 2. 对下文的继续分析可知, 数据缓存时, 以requestKey作为key, 然后删除时, 通过RequestKey
         *    删除对应的CacheHeader;    
         * 3. 删除模块<3.2>
         */
        pruneIfNeeded(entry.data.length);
        /**
         * 1. 对数据进行缓存, 此时缓存包括两部分:
         *    (1) 通过File完成磁盘缓存;
         *    (2) 通过mEntries完成内存缓存;
         * 2. 其实还有一个是网络缓存, 即把获取的数据的响应头缓存起来, 再次请求相同Request时
         *    带上响应头数据, 并判断返回结果, 如果结果为304, 则说明服务器端的缓存数据没有发生变化,
         *    此时直接从内存中读取数据;
         */
        File file = getFileForKey(key);
        BufferedOutputStream fos = new BufferedOutputStream(createOutputStream(file));
        CacheHeader e = new CacheHeader(key, entry);
        /**
         * 缓存到底缓存了哪些内容? 模块<3.3>
         */
        boolean success = e.writeHeader(fos);
        fos.write(entry.data);
        fos.close();
        putEntry(key, e);
        return;
    }
}
3.2 DiskBasedCache.pruneIfNeeded:
public class DiskBasedCache implements Cache {
    private void pruneIfNeeded(int neededSpace) {
        /**
         * 如果缓存容量没有达到上限, 则不做处理;
         */
        if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
            return;
        }
        int prunedFiles = 0;
        /**
         * mEntries为LinkedHashMap, 其内部有一个链表, put或get操作如果操作成功, 
         * 则将对应的Node置于链表尾部, 删除时从表头开始循环获取删除;
         */
        Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, CacheHeader> entry = iterator.next();
            CacheHeader e = entry.getValue();
            /**
             * 对缓存数据进行删除;模块<//1--->>
             */
            boolean deleted = getFileForKey(e.key).delete();
            if (deleted) {
                mTotalSize -= e.size;
            } 
            iterator.remove();
            prunedFiles++;
            if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
                break;
            }
        }
    }

    public File getFileForKey(String key) {
        return new File(mRootDirectory, getFilenameForKey(key));
    }
//1--->
    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;
    }
}
3.3 CacheHeader.put:
static class CacheHeader {

    // http响应首部中用于缓存新鲜度验证的ETag
    final String etag;

    // http响应首部中的响应产生时间
    final long serverDate;

    // 缓存的过期时间
    final long ttl;

    // 缓存的新鲜时间
    final long softTtl;

    // 响应的Headers
    final List<Header> allResponseHeaders;    

    CacheHeader(String key, Entry entry) {
        this(key, entry.etag, entry.serverDate, entry.lastModified, entry.ttl, entry.softTtl,
                    getAllResponseHeaders(entry));
        size = entry.data.length;
    }

    private CacheHeader(String key, String etag, long serverDate, long lastModified, long ttl,
                            long softTtl, List<Header> allResponseHeaders) {
        this.key = key;
        this.etag = ("".equals(etag)) ? null : etag;
        this.serverDate = serverDate;
        this.lastModified = lastModified;
        this.ttl = ttl;
        this.softTtl = softTtl;
        this.allResponseHeaders = allResponseHeaders;
    }
}
3.4 DiskBaseCache.initialize:
public class DiskBasedCache implements Cache {
    @Override
    public synchronized void initialize() {
        ...
        File[] files = mRootDirectory.listFiles();
        if (files == null) {
            return;
        }
        /**
         * 读取指定目录下缓存文件, 然后通过putEntry将数据进行内存缓存, 在读取缓存时其实没有进行
         * totalSize与MaxSize比较, 这是因为在进行缓存时, 已经做了限制最大容量不能超过MaxSize, 
         * 所以这里的totalSize < MaxSize一定成立;
         */
        for (File file : files) {
            long entrySize = file.length();
            CountingInputStream cis = new CountingInputStream(
                        new BufferedInputStream(createInputStream(file)), entrySize);
            try {
                CacheHeader entry = CacheHeader.readHeader(cis);
                entry.size = entrySize;
                putEntry(entry.key, entry);
            } finally {
                cis.close();
            }
        }
    }

    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);
    }
}
3.5 Request.parseNetworkResponse:
public class StringRequest extends Request<String> {
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

public class HttpHeaderParser {
    public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
        long now = System.currentTimeMillis();

        Map<String, String> headers = response.headers;
        /**
         * 1. 后续代码都是为了给这几个变量赋值;
         * 2. 关于下面几个变量, 在<https://www.jianshu.com/p/064a51cdd79b>中进行了总结;
         */
        long serverDate = 0;
        long lastModified = 0;
        long serverExpires = 0;
        long softExpire = 0;
        long finalExpire = 0;
        long maxAge = 0;
        long staleWhileRevalidate = 0;
        boolean hasCacheControl = false;
        boolean mustRevalidate = false;
        String serverEtag = null;
        String headerValue;

        headerValue = headers.get("Date");
        headerValue = headers.get("Cache-Control");
        if (headerValue != null) {
            hasCacheControl = true;
            String[] tokens = headerValue.split(",");
            for (int i = 0; i < tokens.length; i++) {
                String token = tokens[i].trim();
                if (token.equals("no-cache") || token.equals("no-store")) {
                    return null;
                } else if (token.startsWith("max-age=")) {
                    maxAge = Long.parseLong(token.substring(8));
                } else if (token.startsWith("stale-while-revalidate=")) {
                    staleWhileRevalidate = Long.parseLong(token.substring(23));
                } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
                    mustRevalidate = true;
                }
            }
        }
        headerValue = headers.get("Expires");
        ...
        headerValue = headers.get("Last-Modified");
        ...
        serverEtag = headers.get("ETag");
        ...
        return entry;
    }
}
针对响应头, 抓了一下简书首页的包:
https://www.jianshu.com/

相关文章

  • Volley->01基本流程

    相关问题以及参考文章: 1. 为什么说Volley适合数据量小,通信频繁的网络操作 2. 为什么volley不适合...

  • 01-Flask之基本流程

    一、Flask简介 Flask是一个基于python实现的web开发"微"框架。Flask和Django一样,也是...

  • 01-Flask之基本流程

    一、Flask简介 Flask是一个基于python实现的web开发"微"框架。Flask和Django一样,也是...

  • 01-Flask之基本流程

    一、Flask简介 Flask是一个基于python实现的web开发"微"框架。Flask和Django一样,也是...

  • 01-Flask之基本流程

    一、Flask简介 Flask是一个基于python实现的web开发"微"框架。Flask和Django一样,也是...

  • Django - 01-django基本流程

    [toc] 1 安装Django 1.1 Django版本与Python版本对应关系 1.2 安装Django 安...

  • 2019-11-10 循环

    目标 程序的三大流程 while 循环基本使用 break 和 continue while 循环嵌套 01. 程...

  • python 循环结构语句

    目标 程序的三大流程 while 循环基本使用 break 和 continue while 循环嵌套 01. 程...

  • 12 循环

    循环 目标 程序的三大流程 while 循环基本使用 break 和 continue while 循环嵌套 01...

  • 05.Python循环

    目标 程序的三大流程 while 循环基本使用 break 和 continue while 循环嵌套 01. 程...

网友评论

      本文标题:Volley->01基本流程

      本文链接:https://www.haomeiwen.com/subject/wxjelftx.html