美文网首页
Volley源码分析(二)对请求的处理

Volley源码分析(二)对请求的处理

作者: 小川君 | 来源:发表于2018-02-04 19:28 被阅读0次

    Volley源码分析(一)流程

    上篇文章我们走了一遍Volley的大致流程,本章着重对请求的多级取消和缓存作讲解。

    请求的取消

    
        /**
         * Cancels all requests in this queue with the given tag. Tag must be non-null
         * and equality is by identity.
         */
        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;
                }
            });
        }
    

    通过标记取消指定的请求

    
        /**
         * Cancels all requests in this queue for which the given filter applies.
         * @param filter The filtering function to use
         */
        public void cancelAll(RequestFilter filter) {
            synchronized (mCurrentRequests) {
                for (Request<?> request : mCurrentRequests) {
                    if (filter.apply(request)) {
                        request.cancel();
                    }
                }
            }
        }
    

    这两段可以看出,通过遍历mCurrentRequests,取出标识为tag的request,然后将此调用此request的取消方法。

        /**
         * Mark this request as canceled.
         *
         * <p>No callback will be delivered as long as either:
         * <ul>
         *     <li>This method is called on the same thread as the {@link ResponseDelivery} is running
         *     on. By default, this is the main thread.
         *     <li>The request subclass being used overrides cancel() and ensures that it does not
         *     invoke the listener in {@link #deliverResponse} after cancel() has been called in a
         *     thread-safe manner.
         * </ul>
         *
         * <p>There are no guarantees if both of these conditions aren't met.
         */
        // @CallSuper
        public void cancel() {
            synchronized (mLock) {
                mCanceled = true;
                mErrorListener = null;
            }
        }
    
        /**
         * Returns true if this request has been canceled.
         */
        public boolean isCanceled() {
            synchronized (mLock) {
                return mCanceled;
            }
        }
    

    然后设置request的变量mCanceled=true,外界通过isCanceled来获取request的状态。
    共有四个处方调用了isCanceled:CacheDispatcher、RequestFuture、ExecutorDelivery、NetworkDispatcher,抛开RequestFuture不讲。
    CacheDispatcher和NetworkDispathcer是在请求前进行判断的,ExecutorDelivery是在请求成功后进行判断的。
    CacheDispathcher和NetworkDispathcer中,当从缓存队列或网络请求队列中取出一个请求,如果这个请求被取消则不进行网络请求,以CacheDispathcher为例:

       final Request<?> request = mCacheQueue.take();
            request.addMarker("cache-queue-take");
    
            // If the request has been canceled, don't bother dispatching it.
            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                return;
            }
    

    ExecutorDelivery中,当请求成功后,判断当前请求时是否被取消,如果已经被取消,则网络请求返回的数据不返回到主线程:

        // If this request has canceled, finish it and don't deliver.
                if (mRequest.isCanceled()) {
                    mRequest.finish("canceled-at-delivery");
                    return;
                }
    
                // Deliver a normal response or error, depending.
                if (mResponse.isSuccess()) {
                    mRequest.deliverResponse(mResponse.result);
                } else {
                    mRequest.deliverError(mResponse.error);
                }
    

    我们看到这三个地方都调用了request的finish方法:

     /**
         * Notifies the request queue that this request has finished (successfully or with error).
         *
         * <p>Also dumps all events from this request's event log; for debugging.</p>
         */
        void finish(final String tag) {
            if (mRequestQueue != null) {
                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(Request.this.toString());
                        }
                    });
                    return;
                }
    
                mEventLog.add(tag, threadId);
                mEventLog.finish(this.toString());
            }
        }
    

    每个请求都持有RequsetQueue对象,然后调用其finish方法:

      /**
         * Called from {@link Request#finish(String)}, indicating that processing of the given request
         * has finished.
         */
        <T> void finish(Request<T> request) {
            // Remove from the set of requests currently being processed.
            synchronized (mCurrentRequests) {
                mCurrentRequests.remove(request);
            }
        }
    

    将requestQueue中的请求集合删掉此请求,至此请求的取消完全结束。

    缓存

    数据缓存

    在实例化requestQueue的时候 volley创建了一个缓存对象:

    
        private static RequestQueue newRequestQueue(Context context, Network network) {
            File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
            RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
            queue.start();
            return queue;
        }
    

    我们可以去看下DisBasedCache的内部结构:

    private final Map<String, CacheHeader> mEntries =
                new LinkedHashMap<String, CacheHeader>(16, .75f, true);
    
        /** Total amount of space currently used by the cache in bytes. */
        private long mTotalSize = 0;
    
        /** The root directory to use for the cache. */ 
        缓存根目录
        private final File mRootDirectory;
    
        /** The maximum size of the cache in bytes. */
        // 缓存的最大磁盘空间值
        private final int mMaxCacheSizeInBytes;
    
        /** Default maximum disk usage in bytes. */
        // 缓存的默认磁盘空间值
        private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
    
        /** High water mark percentage for the cache */
        private static final float HYSTERESIS_FACTOR = 0.9f;
    
        /** Magic number for current version of cache file format. */
        private static final int CACHE_MAGIC = 0x20150306;
    
    
        //VisibleForTesting
        static class CacheHeader {
            /** The size of the data identified by this CacheHeader. (This is not
             * serialized to disk. */
            long size;
    
            /** The key that identifies the cache entry. */
            final String key;
    
            /** ETag for cache coherence. */
            缓存标签
            final String etag;
    
            /** Date of this response as reported by the server. */
            服务端返回数据的主要内容,当然也是要存储的内容
            final long serverDate;
    
            /** The last modified date for the requested object. */
             服务器返回数据的时间
            final long lastModified;
    
            /** TTL for this record. */
             缓存过期时间
            final long ttl;
    
            /** Soft TTL for this record. */
            final long softTtl;
    
            /** Headers from the response resulting in this cache entry. *
            服务端返回的头部信息
            final List<Header> allResponseHeaders;
        }
    
    
    

    除此之外 还有基类Cache中的一个内部类:

    
        /**
         * Data and metadata for an entry returned by the cache.
         */
        class Entry {
            /** The data returned from cache. */
            public byte[] data;
    
            /** ETag for cache coherency. */
            public String etag;
            /** Date of this response as reported by the server. */
            public long serverDate;
    
            /** The last modified date for the requested object. */
           
            public long lastModified;
    
            /** TTL for this record. */
            public long ttl;
    
            /** Soft TTL for this record. */
            public long softTtl;
    
            /**
             * Response headers as received from server; must be non-null. Should not be mutated
             * directly.
             *
             * <p>Note that if the server returns two headers with the same (case-insensitive) name,
             * this map will only contain the one of them. {@link #allResponseHeaders} may contain all
             * headers if the {@link Cache} implementation supports it.
             */
    
            public Map<String, String> responseHeaders = Collections.emptyMap();
    
            /**
             * All response headers. May be null depending on the {@link Cache} implementation. Should
             * not be mutated directly.
             */
            public List<Header> allResponseHeaders;
    
            /** True if the entry is expired. */
            public boolean isExpired() {
                return this.ttl < System.currentTimeMillis();
            }
    
            /** True if a refresh is needed from the original data source. */
            public boolean refreshNeeded() {
                return this.softTtl < System.currentTimeMillis();
            }
        }
    

    DiskBasedCache中的内部类CacheHeader中的部分参数与基类Cache中的内部类Entity的参数是完全一样,具体原因下一篇文章会有提到,这里简单说一下,

        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;
            }
    
            /**
             * Instantiates a new CacheHeader object.
             * @param key The key that identifies the cache entry
             * @param entry The cache entry.
             */
            CacheHeader(String key, Entry entry) {
                this(key, entry.etag, entry.serverDate, entry.lastModified, entry.ttl, entry.softTtl,
                        getAllResponseHeaders(entry));
                size = entry.data.length;
            }
    

    再cacheHeader的构造器中,通过传进来一个Entity类型的参数,然后将Entity中的参数全部都赋值到CacheHeader中的参数内。
    在CacheHeader中有put和putEntity两个方法,顾名思义,都是传入Entity的方法,但是public类型的方法是put,所以put是从外部调用的最初方法:

    
        /**
         * Puts the entry with the specified key into the cache.
         */
        @Override
        public synchronized void put(String key, Entry entry) {
            pruneIfNeeded(entry.data.length);
            File file = getFileForKey(key);
            try {
                BufferedOutputStream fos = new BufferedOutputStream(createOutputStream(file));
                CacheHeader e = new CacheHeader(key, entry);
                boolean success = e.writeHeader(fos);
                if (!success) {
                    fos.close();
                    VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());
                    throw new IOException();
                }
                fos.write(entry.data);
                fos.close();
                putEntry(key, e);
                return;
            } catch (IOException e) {
            }
            boolean deleted = file.delete();
            if (!deleted) {
                VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
            }
        }
    

    可以看到,进入put首先判断将eneity.data的字节长度做为参数传入了pruneIfNeeded方法,我们简单看下:

    
        /**
         * Prunes the cache to fit the amount of bytes specified.
         * @param neededSpace The amount of bytes we are trying to fit into the cache.
         */
        private void pruneIfNeeded(int neededSpace) {
            if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
                return;
            }
            if (VolleyLog.DEBUG) {
                VolleyLog.v("Pruning old cache entries.");
            }
    
            long before = mTotalSize;
            int prunedFiles = 0;
            long startTime = SystemClock.elapsedRealtime();
    
            Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, CacheHeader> entry = iterator.next();
                CacheHeader e = entry.getValue();
                boolean deleted = getFileForKey(e.key).delete();
                if (deleted) {
                    mTotalSize -= e.size;
                } else {
                   VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
                           e.key, getFilenameForKey(e.key));
                }
                iterator.remove();
                prunedFiles++;
    
                if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
                    break;
                }
            }
        }
    

    这个方法主要是判断缓存空间是否足够放下本次的entity.data,如果能放下则直接返回,如果不能放下,则需要删除一些缓存文件,直到在放下entity.data之后还剩余至少10%的空间量,其中mTotalSize为缓存的总字节长度。
    回到put方法我们继续看,首先根据key值也就是url创建一个file对象,然后以流的方式将cacheHeader中的标识参数写入到file中,完成之后再讲entity.data写入到文件中,最后通过putEntity计算刷新缓存文件的总字节长度。
    除了put之外,还有get、remove、clear三个方法,内容不再多说。

    相关文章

      网友评论

          本文标题:Volley源码分析(二)对请求的处理

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