这是一些cache缓存的基本接口,没有身体特别的地方

package com.android.volley;import java.util.Collections;
import java.util.List;
import java.util.Map;/** An interface for a cache keyed by a String with a byte array as data. */
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*/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.*/void put(String key, Entry entry);/*** Performs any potentially long-running actions needed to initialize the cache; will be called* from a worker thread.*/void initialize();/*** Invalidates an entry in the cache.** @param key Cache key* @param fullExpire True to fully expire the entry, false to soft expire*/void invalidate(String key, boolean fullExpire);/*** Removes an entry from the cache.** @param key Cache key*/void remove(String key);/** Empties the cache. */void clear();/** 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();}}
}

缓存分发类 CacheDispatcher


package com.android.volley;import android.os.Process;
import android.support.annotation.VisibleForTesting;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;/*** Provides a thread for performing cache triage on a queue of requests.** <p>Requests added to the specified cache queue are resolved from cache. Any deliverable response* is posted back to the caller via a {@link ResponseDelivery}. Cache misses and responses that* require refresh are enqueued on the specified network queue for processing by a {@link* NetworkDispatcher}.**/
public class CacheDispatcher extends Thread {private static final boolean DEBUG = VolleyLog.DEBUG;/** The queue of requests coming in for triage. */private final BlockingQueue<Request<?>> mCacheQueue;/** The queue of requests going out to the network. */private final BlockingQueue<Request<?>> mNetworkQueue;/** The cache to read from. */private final Cache mCache;/** For posting responses. */private final ResponseDelivery mDelivery;/** Used for telling us to die. */private volatile boolean mQuit = false;/** Manage list of waiting requests and de-duplicate requests with same cache key. */private final WaitingRequestManager mWaitingRequestManager;/*** Creates a new cache triage dispatcher thread. You must call {@link #start()} in order to* begin processing.** @param cacheQueue Queue of incoming requests for triage* @param networkQueue Queue to post requests that require network to* @param cache Cache interface to use for resolution* @param delivery Delivery interface to use for posting responses*/public CacheDispatcher(BlockingQueue<Request<?>> cacheQueue,BlockingQueue<Request<?>> networkQueue,Cache cache,ResponseDelivery delivery) {mCacheQueue = cacheQueue;mNetworkQueue = networkQueue;mCache = cache;mDelivery = delivery;mWaitingRequestManager = new WaitingRequestManager(this);}/*** Forces this dispatcher to quit immediately. If any requests are still in the queue, they are* not guaranteed to be processed.*/public void quit() {mQuit = true;interrupt();}@Overridepublic void run() {if (DEBUG) VolleyLog.v("start new dispatcher");Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// Make a blocking call to initialize the cache.mCache.initialize();while (true) {try {processRequest();} catch (InterruptedException e) {// We may have been interrupted because it was time to quit.if (mQuit) {Thread.currentThread().interrupt();return;}VolleyLog.e("Ignoring spurious interrupt of CacheDispatcher thread; "+ "use quit() to terminate it");}}}// Extracted to its own method to ensure locals have a constrained liveness scope by the GC.// This is needed to avoid keeping previous request references alive for an indeterminate amount// of time. Update consumer-proguard-rules.pro when modifying this. See also// https://github.com/google/volley/issues/114private void processRequest() throws InterruptedException {// Get a request from the cache triage queue, blocking until// at least one is available.final Request<?> request = mCacheQueue.take();processRequest(request);}@VisibleForTestingvoid processRequest(final Request<?> request) throws InterruptedException {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;}// Attempt to retrieve this item from cache.Cache.Entry entry = mCache.get(request.getCacheKey());if (entry == null) {request.addMarker("cache-miss");// Cache miss; send off to the network dispatcher.if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {mNetworkQueue.put(request);}return;}// If it is completely expired, just send it to the network.if (entry.isExpired()) {request.addMarker("cache-hit-expired");request.setCacheEntry(entry);if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {mNetworkQueue.put(request);}return;}// We have a cache hit; parse its data for delivery back to the request.request.addMarker("cache-hit");Response<?> response =request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));request.addMarker("cache-hit-parsed");if (!entry.refreshNeeded()) {// Completely unexpired cache hit. Just deliver the response.mDelivery.postResponse(request, response);} else {// Soft-expired cache hit. We can deliver the cached response,// but we need to also send the request to the network for// refreshing.request.addMarker("cache-hit-refresh-needed");request.setCacheEntry(entry);// Mark the response as intermediate.response.intermediate = true;if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {// Post the intermediate response back to the user and have// the delivery then forward the request along to the network.mDelivery.postResponse(request,response,new Runnable() {@Overridepublic void run() {try {mNetworkQueue.put(request);} catch (InterruptedException e) {// Restore the interrupted statusThread.currentThread().interrupt();}}});} else {// request has been added to list of waiting requests// to receive the network response from the first request once it returns.mDelivery.postResponse(request, response);}}}private static class WaitingRequestManager implements Request.NetworkRequestCompleteListener {/*** Staging area for requests that already have a duplicate request in flight.** <ul>*   <li>containsKey(cacheKey) indicates that there is a request in flight for the given*       cache key.*   <li>get(cacheKey) returns waiting requests for the given cache key. The in flight*       request is <em>not</em> contained in that list. Is null if no requests are staged.* </ul>*/private final Map<String, List<Request<?>>> mWaitingRequests = new HashMap<>();private final CacheDispatcher mCacheDispatcher;WaitingRequestManager(CacheDispatcher cacheDispatcher) {mCacheDispatcher = cacheDispatcher;}/** Request received a valid response that can be used by other waiting requests. */@Overridepublic void onResponseReceived(Request<?> request, Response<?> response) {if (response.cacheEntry == null || response.cacheEntry.isExpired()) {onNoUsableResponseReceived(request);return;}String cacheKey = request.getCacheKey();List<Request<?>> waitingRequests;synchronized (this) {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.for (Request<?> waiting : waitingRequests) {mCacheDispatcher.mDelivery.postResponse(waiting, response);}}}/** No valid response received from network, release waiting requests. */@Overridepublic synchronized void onNoUsableResponseReceived(Request<?> request) {String cacheKey = request.getCacheKey();List<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);if (waitingRequests != null && !waitingRequests.isEmpty()) {if (VolleyLog.DEBUG) {VolleyLog.v("%d waiting requests for cacheKey=%s; resend to network",waitingRequests.size(), cacheKey);}Request<?> nextInLine = waitingRequests.remove(0);mWaitingRequests.put(cacheKey, waitingRequests);nextInLine.setNetworkRequestCompleteListener(this);try {mCacheDispatcher.mNetworkQueue.put(nextInLine);} catch (InterruptedException iex) {VolleyLog.e("Couldn't add request to queue. %s", iex.toString());// Restore the interrupted status of the calling thread (i.e. NetworkDispatcher)Thread.currentThread().interrupt();// Quit the current CacheDispatcher thread.mCacheDispatcher.quit();}}}/*** For cacheable requests, if a request for the same cache key is already in flight, add it* to a queue to wait for that in-flight request to finish.** @return whether the request was queued. If false, we should continue issuing the request*     over the network. If true, we should put the request on hold to be processed when the*     in-flight request finishes.*/private synchronized boolean maybeAddToWaitingRequests(Request<?> request) {String cacheKey = request.getCacheKey();// Insert request into stage if there's already a request with the same cache key// in flight.if (mWaitingRequests.containsKey(cacheKey)) {// There is already a request in flight. Queue up.List<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);if (stagedRequests == null) {stagedRequests = new ArrayList<>();}request.addMarker("waiting-for-response");stagedRequests.add(request);mWaitingRequests.put(cacheKey, stagedRequests);if (VolleyLog.DEBUG) {VolleyLog.d("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);}return true;} else {// Insert 'null' queue for this cacheKey, indicating there is now a request in// flight.mWaitingRequests.put(cacheKey, null);request.setNetworkRequestCompleteListener(this);if (VolleyLog.DEBUG) {VolleyLog.d("new request, sending to network %s", cacheKey);}return false;}}}
}

maybeAddToWaitingRequests() 这个方法值得去学习,通过map添加list,添加请求队列。

没有缓存的类

package com.android.volley.toolbox;import com.android.volley.Cache;/** A cache that doesn't. */
public class NoCache implements Cache {@Overridepublic void clear() {}@Overridepublic Entry get(String key) {return null;}@Overridepublic void put(String key, Entry entry) {}@Overridepublic void invalidate(String key, boolean fullExpire) {}@Overridepublic void remove(String key) {}@Overridepublic void initialize() {}
}

文件类添加删除缓存操作

package com.android.volley.toolbox;import android.os.SystemClock;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import com.android.volley.Cache;
import com.android.volley.Header;
import com.android.volley.VolleyLog;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;/*** Cache implementation that caches files directly onto the hard disk in the specified directory.* The default disk usage size is 5MB, but is configurable.** <p>This cache supports the {@link Entry#allResponseHeaders} headers field.*/
public class DiskBasedCache implements Cache {/** Map of the Key, CacheHeader pairs */private final Map<String, CacheHeader> mEntries = new LinkedHashMap<>(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;/*** Constructs an instance of the DiskBasedCache at the specified directory.** @param rootDirectory The root directory of the cache.* @param maxCacheSizeInBytes The maximum size of the cache in bytes.*/public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {mRootDirectory = rootDirectory;mMaxCacheSizeInBytes = maxCacheSizeInBytes;}/*** Constructs an instance of the DiskBasedCache at the specified directory using the default* maximum cache size of 5MB.** @param rootDirectory The root directory of the cache.*/public DiskBasedCache(File rootDirectory) {this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);}/** Clears the cache. Deletes all cached files from disk. */@Overridepublic synchronized void clear() {File[] files = mRootDirectory.listFiles();if (files != null) {for (File file : files) {file.delete();}}mEntries.clear();mTotalSize = 0;VolleyLog.d("Cache cleared.");}/** Returns the cache entry with the specified key if it exists, null otherwise. */@Overridepublic 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);try {CountingInputStream cis =new CountingInputStream(new BufferedInputStream(createInputStream(file)), file.length());try {CacheHeader entryOnDisk = CacheHeader.readHeader(cis);if (!TextUtils.equals(key, entryOnDisk.key)) {// File was shared by two keys and now holds data for a different entry!VolleyLog.d("%s: key=%s, found=%s", file.getAbsolutePath(), key, entryOnDisk.key);// Remove key whose contents on disk have been replaced.removeEntry(key);return null;}byte[] data = streamToBytes(cis, cis.bytesRemaining());return entry.toCacheEntry(data);} finally {// Any IOException thrown here is handled by the below catch block by design.//noinspection ThrowFromFinallyBlockcis.close();}} catch (IOException e) {VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());remove(key);return null;}}/*** Initializes the DiskBasedCache by scanning for all files currently in the specified root* directory. Creates the root directory if necessary.*/@Overridepublic 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) {try {long entrySize = file.length();CountingInputStream cis =new CountingInputStream(new BufferedInputStream(createInputStream(file)), entrySize);try {CacheHeader entry = CacheHeader.readHeader(cis);// NOTE: When this entry was put, its size was recorded as data.length, but// when the entry is initialized below, its size is recorded as file.length()entry.size = entrySize;putEntry(entry.key, entry);} finally {// Any IOException thrown here is handled by the below catch block by design.//noinspection ThrowFromFinallyBlockcis.close();}} catch (IOException e) {//noinspection ResultOfMethodCallIgnoredfile.delete();}}}/*** Invalidates an entry in the cache.** @param key Cache key* @param fullExpire True to fully expire the entry, false to soft expire*/@Overridepublic 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);}}/** Puts the entry with the specified key into the cache. */@Overridepublic 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());}}/** Removes the specified key from the cache if it exists. */@Overridepublic 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));}}/*** Creates a pseudo-unique filename for the specified cache key.** @param key The key to generate a file name for.* @return A pseudo-unique filename.*/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));}/*** 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;}}if (VolleyLog.DEBUG) {VolleyLog.v("pruned %d files, %d bytes, %d ms",prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime);}}/*** Puts the entry with the specified key into the cache.** @param key The key to identify the entry by.* @param entry The entry to cache.*/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);}/** Removes the entry identified by 'key' from the cache. */private void removeEntry(String key) {CacheHeader removed = mEntries.remove(key);if (removed != null) {mTotalSize -= removed.size;}}/*** Reads length bytes from CountingInputStream into byte array.** @param cis input stream* @param length number of bytes to read* @throws IOException if fails to read all bytes*/// VisibleForTestingstatic byte[] streamToBytes(CountingInputStream cis, long length) throws IOException {long maxLength = cis.bytesRemaining();// Length cannot be negative or greater than bytes remaining, and must not overflow int.if (length < 0 || length > maxLength || (int) length != length) {throw new IOException("streamToBytes length=" + length + ", maxLength=" + maxLength);}byte[] bytes = new byte[(int) length];new DataInputStream(cis).readFully(bytes);return bytes;}// VisibleForTestingInputStream createInputStream(File file) throws FileNotFoundException {return new FileInputStream(file);}// VisibleForTestingOutputStream createOutputStream(File file) throws FileNotFoundException {return new FileOutputStream(file);}/** Handles holding onto the cache headers for an entry. */// VisibleForTestingstatic 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;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;}private static List<Header> getAllResponseHeaders(Entry entry) {// If the entry contains all the response headers, use that field directly.if (entry.allResponseHeaders != null) {return entry.allResponseHeaders;}// Legacy fallback - copy headers from the map.return HttpHeaderParser.toAllHeaderList(entry.responseHeaders);}/*** Reads the header from a CountingInputStream and returns a CacheHeader object.** @param is The InputStream to read from.* @throws IOException if fails to read header*/static CacheHeader readHeader(CountingInputStream is) throws IOException {int magic = readInt(is);if (magic != CACHE_MAGIC) {// don't bother deleting, it'll get pruned eventuallythrow new IOException();}String key = readString(is);String etag = readString(is);long serverDate = readLong(is);long lastModified = readLong(is);long ttl = readLong(is);long softTtl = readLong(is);List<Header> allResponseHeaders = readHeaderList(is);return new CacheHeader(key, etag, serverDate, lastModified, ttl, softTtl, allResponseHeaders);}/** Creates a cache entry for the specified data. */Entry toCacheEntry(byte[] data) {Entry e = new Entry();e.data = data;e.etag = etag;e.serverDate = serverDate;e.lastModified = lastModified;e.ttl = ttl;e.softTtl = softTtl;e.responseHeaders = HttpHeaderParser.toHeaderMap(allResponseHeaders);e.allResponseHeaders = Collections.unmodifiableList(allResponseHeaders);return e;}/** Writes the contents of this CacheHeader to the specified OutputStream. */boolean writeHeader(OutputStream os) {try {writeInt(os, CACHE_MAGIC);writeString(os, key);writeString(os, etag == null ? "" : etag);writeLong(os, serverDate);writeLong(os, lastModified);writeLong(os, ttl);writeLong(os, softTtl);writeHeaderList(allResponseHeaders, os);os.flush();return true;} catch (IOException e) {VolleyLog.d("%s", e.toString());return false;}}}@VisibleForTestingstatic class CountingInputStream extends FilterInputStream {private final long length;private long bytesRead;CountingInputStream(InputStream in, long length) {super(in);this.length = length;}@Overridepublic int read() throws IOException {int result = super.read();if (result != -1) {bytesRead++;}return result;}@Overridepublic int read(byte[] buffer, int offset, int count) throws IOException {int result = super.read(buffer, offset, count);if (result != -1) {bytesRead += result;}return result;}@VisibleForTestinglong bytesRead() {return bytesRead;}long bytesRemaining() {return length - bytesRead;}}/** Homebrewed simple serialization system used for reading and writing cache* headers on disk. Once upon a time, this used the standard Java* Object{Input,Output}Stream, but the default implementation relies heavily* on reflection (even for standard types) and generates a ton of garbage.** TODO: Replace by standard DataInput and DataOutput in next cache version.*//*** Simple wrapper around {@link InputStream#read()} that throws EOFException instead of* returning -1.*/private static int read(InputStream is) throws IOException {int b = is.read();if (b == -1) {throw new EOFException();}return b;}static void writeInt(OutputStream os, int n) throws IOException {os.write((n >> 0) & 0xff);os.write((n >> 8) & 0xff);os.write((n >> 16) & 0xff);os.write((n >> 24) & 0xff);}static int readInt(InputStream is) throws IOException {int n = 0;n |= (read(is) << 0);n |= (read(is) << 8);n |= (read(is) << 16);n |= (read(is) << 24);return n;}static void writeLong(OutputStream os, long n) throws IOException {os.write((byte) (n >>> 0));os.write((byte) (n >>> 8));os.write((byte) (n >>> 16));os.write((byte) (n >>> 24));os.write((byte) (n >>> 32));os.write((byte) (n >>> 40));os.write((byte) (n >>> 48));os.write((byte) (n >>> 56));}static long readLong(InputStream is) throws IOException {long n = 0;n |= ((read(is) & 0xFFL) << 0);n |= ((read(is) & 0xFFL) << 8);n |= ((read(is) & 0xFFL) << 16);n |= ((read(is) & 0xFFL) << 24);n |= ((read(is) & 0xFFL) << 32);n |= ((read(is) & 0xFFL) << 40);n |= ((read(is) & 0xFFL) << 48);n |= ((read(is) & 0xFFL) << 56);return n;}static void writeString(OutputStream os, String s) throws IOException {byte[] b = s.getBytes("UTF-8");writeLong(os, b.length);os.write(b, 0, b.length);}static String readString(CountingInputStream cis) throws IOException {long n = readLong(cis);byte[] b = streamToBytes(cis, n);return new String(b, "UTF-8");}static void writeHeaderList(List<Header> headers, OutputStream os) throws IOException {if (headers != null) {writeInt(os, headers.size());for (Header header : headers) {writeString(os, header.getName());writeString(os, header.getValue());}} else {writeInt(os, 0);}}static List<Header> readHeaderList(CountingInputStream cis) throws IOException {int size = readInt(cis);if (size < 0) {throw new IOException("readHeaderList size=" + size);}List<Header> result =(size == 0) ? Collections.<Header>emptyList() : new ArrayList<Header>();for (int i = 0; i < size; i++) {String name = readString(cis).intern();String value = readString(cis).intern();result.add(new Header(name, value));}return result;}
}

Volley源码学习2--cache类相关推荐

  1. JDK11源码学习05 | HashMap类

    JDK11源码学习05 | HashMap类 JDK11源码学习01 | Map接口 JDK11源码学习02 | AbstractMap抽象类 JDK11源码学习03 | Serializable接口 ...

  2. Volley源码学习2--Error类

    Volley 网络请求错误返回 VolleyError package com.android.volley;/** Exception style class encapsulating Volle ...

  3. Mono源码学习笔记:Console类(四)

    NullStream 类 (internal class) 以下就是 mcs/class/corlib/System.IO/NullStream.cs: 01: namespace System.IO ...

  4. JAVA源码学习(一)——String类

    一.String类的不可变性 源码: public final class String//final修饰类,不可继承 private final char value[];//String类实际返回 ...

  5. Volley源码学习3--log类

    VolleyLog工具类 这个类对系统Log做了一些封装,支持release不打印log,而且可以VolleyLog.d("xxxx");已经一个d% s%格式的log packa ...

  6. Mono源码学习笔记:Console类(三)

    Buffer 类 (public static class) 以下就是 mcs/class/corlib/System/Buffer.cs: 001: // 002: // System.Buffer ...

  7. Volley源码学习1--volley结构图

    volley结构图 从这张图可以了解volley整个工作原理. 1 当客户端发生一个请求的时候 2 会先从缓存中去查找,是不是有缓存 3 如果请求不能从缓存中得到服务,那么它将被放置在网络队列中.第一 ...

  8. PHP Yac cache 源码学习笔记

    YAC 源码学习笔记 本文地址 http://blog.csdn.net/fanhengguang_php/article/details/54863955 config.m4 检测系统共享内存支持情 ...

  9. 基于Qt5.14.2和mingw的Qt源码学习(三) — 元对象系统简介及moc工具是如何保存类属性和方法的

    基于Qt5.14.2和mingw的Qt源码学习(三) - 元对象系统简介及moc工具是如何保存类属性和方法的 一.什么是元对象系统 1.元对象系统目的 2.实现元对象系统的关键 3.元对象系统的其他一 ...

最新文章

  1. redirect和forward的区别
  2. 馀承东鸿蒙发布会,余承东确认出席发布会!荣耀智慧屏-首发搭载鸿蒙系统
  3. Java如何解析markdown_使用Java实现的一款Markdown解析器md2x
  4. 在指定位置上方出现通用jquery悬浮提示框插件全站通用
  5. oracle 运维案例,运维注意事项及案例讲解(个人心得)
  6. NopCommerce开源项目中很基础但是很实用的C# Helper方法
  7. php正则替换p闭合标签,php正则替换标签的实现方法
  8. 为脚本语言平反-JavaScript篇(3)
  9. base64解密后乱码_php实现php代码的加密解密
  10. Java 会是首选的最佳编程语言吗?
  11. 基础Floyd--任意两点间最短路
  12. 数据结构十大排序算法(python)
  13. 深度解密换脸应用Deepfake
  14. [强化学习实战]深度Q学习-DQN算法原理
  15. vsscanf用法解析
  16. (免费配音软件)[配音助手 更新] (1.3版本) 阿里云配音软件
  17. volatile有序性的真正作用
  18. DBeaver21.1.5如何迁移已有数据库连接
  19. 【转】小玄子和小桂子
  20. 关于GCC属性中的弱符号(weak symbol)

热门文章

  1. java中的文本框_java里的JTextField文本框怎么设置大小?
  2. k means聚类算法_K-Means 聚类算法 20210108
  3. 防止对SQL Server的蛮力攻击
  4. .NET的RedisProvider
  5. linux编译c 自动化,Linux江湖06:感悟GNU C以及将Vim打造成C/C++的半自动化IDE
  6. sqlparameter多个赋值一行完成_HashMap源码从面试题说起:请一行一行代码描述hashmap put方法...
  7. 过滤html标签 去除html标签
  8. matlab fopen wt,matlab的fopen和fprintf
  9. c语言 define宏名称if(参数),宏编程基础内容
  10. php读取pdf文件乱码,使用php读取pdf文件