mobile/android/base/favicons/cache/FaviconCache.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.favicons.cache;
     7 import android.graphics.Bitmap;
     8 import android.util.Log;
     9 import org.mozilla.gecko.favicons.Favicons;
    11 import java.util.Iterator;
    12 import java.util.LinkedList;
    13 import java.util.concurrent.ConcurrentHashMap;
    14 import java.util.concurrent.Semaphore;
    15 import java.util.concurrent.atomic.AtomicInteger;
    17 /**
    18  * Implements a Least-Recently-Used cache for Favicons, keyed by Favicon URL.
    19  *
    20  * When a favicon at a particular URL is decoded, it will yield one or more bitmaps.
    21  * While in memory, these bitmaps are stored in a list, sorted in ascending order of size, in a
    22  * FaviconsForURL object.
    23  * The collection of FaviconsForURL objects currently in the cache is stored in backingMap, keyed
    24  * by favicon URL.
    25  *
    26  * A second map exists for permanent cache entries -- ones that are never expired. These entries
    27  * are assumed to be disjoint from those in the normal cache, and this map is checked first.
    28  *
    29  * FaviconsForURL provides a method for obtaining the smallest icon larger than a given size - the
    30  * most appropriate icon for a particular size.
    31  * It also distinguishes between "primary" favicons (Ones that have merely been extracted from a
    32  * file downloaded from the website) and "secondary" favicons (Ones that have been computed locally
    33  * as resized versions of primary favicons.).
    34  *
    35  * FaviconsForURL is also responsible for storing URL-specific, as opposed to favicon-specific,
    36  * information. For the purposes of this cache, the simplifying assumption that the dominant colour
    37  * for all favicons served from a particular favicon URL shall be the same is made. (To violate this
    38  * would mandate serving an ICO or similar file with multiple radically different images in it - an
    39  * ill-advised and extremely uncommon use-case, for sure.)
    40  * The dominant colour information is updated as the element is being added to the cache - typically
    41  * on the background thread.
    42  * Also present here are the download timestamp and isFailed flag. Upon failure, the flag is set.
    43  * A constant exists in this file to specify the maximum time permitted between failures before
    44  * a retry is again permitted.
    45  *
    46  * TODO: Expiry of Favicons from the favicon database cache is not implemented. (Bug 914296)
    47  *
    48  * A typical request to the cache will consist of a Favicon URL and a target size. The FaviconsForURL
    49  * object for that URL will be obtained, queried for a favicon matching exactly the needed size, and
    50  * if successful, the result is returned.
    51  * If unsuccessful, the object is asked to return the smallest available primary favicon larger than
    52  * the target size. If this step works, the result is downscaled to create a new secondary favicon,
    53  * which is then stored (So subsequent requests will succeed at the first step) and returned.
    54  * If that step fails, the object finally walks backwards through its sequence of favicons until it
    55  * finds the largest primary favicon smaller than the target. This is then upscaled by a maximum of
    56  * 2x towards the target size, and the result cached and returned as above.
    57  *
    58  * The bitmaps themselves are encapsulated inside FaviconCacheElement objects. These objects contain,
    59  * as well as the bitmap, a pointer to the encapsulating FaviconsForURL object (Used by the LRU
    60  * culler), the size of the encapsulated image, a flag indicating if this is a primary favicon, and
    61  * a flag indicating if the entry is invalid.
    62  * All FaviconCacheElement objects are tracked in the ordering LinkedList. This is used to record
    63  * LRU information about FaviconCacheElements. In particular, the most recently used FaviconCacheElement
    64  * will be at the start of the list, the least recently used at the end of the list.
    65  *
    66  * When the cache runs out of space, it removes FaviconCacheElements starting from the end of the list
    67  * until a sufficient amount of space has been freed.
    68  * When a secondary favicon is removed in this way, it is simply deleted from its parent FaviconsForURLs
    69  * object's list of available favicons.
    70  * The backpointer field on the FaviconCacheElement is used to remove the element from the encapsulating
    71  * FaviconsForURL object, when this is required.
    72  * When a primary favicon is removed, its invalid flag is set to true and its bitmap payload is set
    73  * to null (So it is available for freeing by the garbage collector). This reduces the memory footprint
    74  * of the icon to essentially zero, but keeps track of which primary favicons exist for this favicon
    75  * URL.
    76  * If a subsequent request comes in for that favicon URL, it is then known that a primary of those
    77  * dimensions is available, just that it is not in the cache. The system is then able to load the
    78  * primary back into the cache from the database (Where the entirety of the initially encapsulating
    79  * container-formatted image file is stored).
    80  * If this were not done, then when processing requests after the culling of primary favicons it would
    81  * be impossible to distinguish between the nonexistence of a primary and the nonexistence of a primary
    82  * in the cache without querying the database.
    83  *
    84  * The implementation is safe to use from multiple threads and, while is it not entirely strongly
    85  * consistent all of the time, you almost certainly don't care.
    86  * The thread-safety implementation used is approximately MRSW with semaphores. An extra semaphore
    87  * is used to grant mutual exclusion over reordering operations from reader threads (Who thus gain
    88  * a quasi-writer status to do such limited mutation as is necessary).
    89  *
    90  * Reads which race with writes are liable to not see the ongoing write. The cache may return a
    91  * stale or now-removed value to the caller. Returned values are never invalid, even in the face
    92  * of concurrent reading and culling.
    93  */
    94 public class FaviconCache {
    95     private static final String LOGTAG = "FaviconCache";
    97     // The number of spaces to allocate for favicons in each node.
    98     private static final int NUM_FAVICON_SIZES = 4;
   100     // Dimensions of the largest favicon to store in the cache. Everything is downscaled to this.
   101     public final int maxCachedWidth;
   103     // Retry failed favicons after 20 minutes.
   104     public static final long FAILURE_RETRY_MILLISECONDS = 1000 * 60 * 20;
   106     // Map relating Favicon URLs with objects representing decoded favicons.
   107     // Since favicons may be container formats holding multiple icons, the underlying type holds a
   108     // sorted list of bitmap payloads in ascending order of size. The underlying type may be queried
   109     // for the least larger payload currently present.
   110     private final ConcurrentHashMap<String, FaviconsForURL> backingMap = new ConcurrentHashMap<String, FaviconsForURL>();
   112     // And the same, but never evicted.
   113     private final ConcurrentHashMap<String, FaviconsForURL> permanentBackingMap = new ConcurrentHashMap<String, FaviconsForURL>();
   115     // A linked list used to implement a queue, defining the LRU properties of the cache. Elements
   116     // contained within the various FaviconsForURL objects are held here, the least recently used
   117     // of which at the end of the list. When space needs to be reclaimed, the appropriate bitmap is
   118     // culled.
   119     private final LinkedList<FaviconCacheElement> ordering = new LinkedList<FaviconCacheElement>();
   121     // The above structures, if used correctly, enable this cache to exhibit LRU semantics across all
   122     // favicon payloads in the system, as well as enabling the dynamic selection from the cache of
   123     // the primary bitmap most suited to the requested size (in cases where multiple primary bitmaps
   124     // are provided by the underlying file format).
   126     // Current size, in bytes, of the bitmap data present in the LRU cache.
   127     private final AtomicInteger currentSize = new AtomicInteger(0);
   129     // The maximum quantity, in bytes, of bitmap data which may be stored in the cache.
   130     private final int maxSizeBytes;
   132     // Tracks the number of ongoing read operations. Enables the first one in to lock writers out and
   133     // the last one out to let them in.
   134     private final AtomicInteger ongoingReads = new AtomicInteger(0);
   136     // Used to ensure transaction fairness - each txn acquires and releases this as the first operation.
   137     // The effect is an orderly, inexpensive ordering enforced on txns to prevent writer starvation.
   138     private final Semaphore turnSemaphore = new Semaphore(1);
   140     // A deviation from the usual MRSW solution - this semaphore is used to guard modification to the
   141     // ordering map. This allows for read transactions to update the most-recently-used value without
   142     // needing to take out the write lock.
   143     private final Semaphore reorderingSemaphore = new Semaphore(1);
   145     // The semaphore one must acquire in order to perform a write.
   146     private final Semaphore writeLock = new Semaphore(1);
   148     /**
   149      * Called by txns performing only reads as they start. Prevents writer starvation with a turn
   150      * semaphore and locks writers out if this is the first concurrent reader txn starting up.
   151      */
   152     private void startRead() {
   153         turnSemaphore.acquireUninterruptibly();
   154         turnSemaphore.release();
   156         if (ongoingReads.incrementAndGet() == 1) {
   157             // First one in. Wait for writers to finish and lock them out.
   158             writeLock.acquireUninterruptibly();
   159         }
   160     }
   162     /**
   163      * Called by transactions performing only reads as they finish. Ensures that if this is the last
   164      * concluding read transaction then then writers are subsequently allowed in.
   165      */
   166     private void finishRead() {
   167         if (ongoingReads.decrementAndGet() == 0) {
   168             writeLock.release();
   169         }
   170     }
   172     /**
   173      * Called by writer transactions upon start. Ensures fairness and then obtains the write lock.
   174      * Upon return, no other txns will be executing concurrently.
   175      */
   176     private void startWrite() {
   177         turnSemaphore.acquireUninterruptibly();
   178         writeLock.acquireUninterruptibly();
   179     }
   181     /**
   182      * Called by a concluding write transaction - unlocks the structure.
   183      */
   184     private void finishWrite() {
   185         turnSemaphore.release();
   186         writeLock.release();
   187     }
   189     public FaviconCache(int maxSize, int maxWidthToCache) {
   190         maxSizeBytes = maxSize;
   191         maxCachedWidth = maxWidthToCache;
   192     }
   194     /**
   195      * Determine if the provided favicon URL is marked as a failure (Has failed to load before -
   196      * such icons get blacklisted for a time to prevent us endlessly retrying.)
   197      *
   198      * @param faviconURL Favicon URL to check if failed in memcache.
   199      * @return true if this favicon is blacklisted, false otherwise.
   200      */
   201     public boolean isFailedFavicon(String faviconURL) {
   202         if (faviconURL == null) {
   203             return true;
   204         }
   206         startRead();
   208         try {
   209             // If we don't have it in the cache, it certainly isn't a known failure.
   210             // Non-evictable favicons are never failed, so we don't need to
   211             // check permanentBackingMap.
   212             if (!backingMap.containsKey(faviconURL)) {
   213                 return false;
   214             }
   216             FaviconsForURL container = backingMap.get(faviconURL);
   218             // If the has failed flag is not set, it's certainly not a known failure.
   219             if (!container.hasFailed) {
   220                 return false;
   221             }
   223             final long failureTimestamp = container.downloadTimestamp;
   225             // Calculate elapsed time since the failing download.
   226             final long failureDiff = System.currentTimeMillis() - failureTimestamp;
   228             // If the expiry is still in effect, return. Otherwise, continue and unmark the failure.
   229             if (failureDiff < FAILURE_RETRY_MILLISECONDS) {
   230                 return true;
   231             }
   232         } catch (Exception unhandled) {
   233             Log.e(LOGTAG, "FaviconCache exception!", unhandled);
   234             return true;
   235         }  finally {
   236             finishRead();
   237         }
   239         startWrite();
   241         // If the entry is no longer failed, remove the record of it from the cache.
   242         try {
   243             recordRemoved(backingMap.remove(faviconURL));
   244             return false;
   245         } finally {
   246             finishWrite();
   247         }
   248     }
   250     /**
   251      * Mark the indicated page URL as a failed Favicon until the provided time.
   252      *
   253      * @param faviconURL Page URL for which a Favicon load has failed.
   254      */
   255     public void putFailed(String faviconURL) {
   256         startWrite();
   258         try {
   259             FaviconsForURL container = new FaviconsForURL(0, true);
   260             recordRemoved(backingMap.put(faviconURL, container));
   261         } finally {
   262             finishWrite();
   263         }
   264     }
   266     /**
   267      * Fetch a Favicon for the given URL as close as possible to the size provided.
   268      * If an icon of the given size is already in the cache, it is returned.
   269      * If an icon of the given size is not in the cache but a larger unscaled image does exist in
   270      * the cache, we downscale the larger image to the target size and cache the result.
   271      * If there is no image of the required size, null is returned.
   272      *
   273      * @param faviconURL The URL for which a Favicon is desired. Must not be null.
   274      * @param targetSize The size of the desired favicon.
   275      * @return A favicon of the requested size for the requested URL, or null if none cached.
   276      */
   277     public Bitmap getFaviconForDimensions(String faviconURL, int targetSize) {
   278         if (faviconURL == null) {
   279             Log.e(LOGTAG, "You passed a null faviconURL to getFaviconForDimensions. Don't.");
   280             return null;
   281         }
   283         boolean shouldComputeColour = false;
   284         boolean wasPermanent = false;
   285         FaviconsForURL container;
   286         final Bitmap newBitmap;
   288         startRead();
   290         try {
   291             container = permanentBackingMap.get(faviconURL);
   292             if (container == null) {
   293                 container = backingMap.get(faviconURL);
   294                 if (container == null) {
   295                     // We don't have it!
   296                     return null;
   297                 }
   298             } else {
   299                 wasPermanent = true;
   300             }
   302             FaviconCacheElement cacheElement;
   304             // If targetSize is -1, it means we want the largest possible icon.
   305             int cacheElementIndex = (targetSize == -1) ? -1 : container.getNextHighestIndex(targetSize);
   307             // cacheElementIndex now holds either the index of the next least largest bitmap from
   308             // targetSize, or -1 if targetSize > all bitmaps.
   309             if (cacheElementIndex != -1) {
   310                 // If cacheElementIndex is not the sentinel value, then it is a valid index into favicons.
   311                 cacheElement = container.favicons.get(cacheElementIndex);
   313                 if (cacheElement.invalidated) {
   314                     return null;
   315                 }
   317                 // If we found exactly what we wanted - we're done.
   318                 if (cacheElement.imageSize == targetSize) {
   319                     setMostRecentlyUsedWithinRead(cacheElement);
   320                     return cacheElement.faviconPayload;
   321                 }
   322             } else {
   323                 // We requested an image larger than all primaries. Set the element to start the search
   324                 // from to the element beyond the end of the array, so the search runs backwards.
   325                 cacheElementIndex = container.favicons.size();
   326             }
   328             // We did not find exactly what we wanted, but now have set cacheElementIndex to the index
   329             // where what we want should live in the list. We now request the next least larger primary
   330             // from the cache. We will downscale this to our target size.
   332             // If there is no such primary, we'll upscale the next least smaller one instead.
   333             cacheElement = container.getNextPrimary(cacheElementIndex);
   335             if (cacheElement == null) {
   336                 // The primary has been invalidated! Fail! Need to get it back from the database.
   337                 return null;
   338             }
   340             if (targetSize == -1) {
   341                 // We got the biggest primary, so that's what we'll return.
   342                 return cacheElement.faviconPayload;
   343             }
   345             // Scaling logic...
   346             Bitmap largestElementBitmap = cacheElement.faviconPayload;
   347             int largestSize = cacheElement.imageSize;
   349             if (largestSize >= targetSize) {
   350                 // The largest we have is larger than the target - downsize to target.
   351                 newBitmap = Bitmap.createScaledBitmap(largestElementBitmap, targetSize, targetSize, true);
   352             } else {
   353                 // Our largest primary is smaller than the desired size. Upscale by a maximum of 2x.
   354                 // largestSize now reflects the maximum size we can upscale to.
   355                 largestSize *= 2;
   357                 if (largestSize >= targetSize) {
   358                     // Perfect! We can upscale by less than 2x and reach the needed size. Do it.
   359                     newBitmap = Bitmap.createScaledBitmap(largestElementBitmap, targetSize, targetSize, true);
   360                 } else {
   361                     // We don't have enough information to make the target size look nonterrible. Best effort:
   362                     newBitmap = Bitmap.createScaledBitmap(largestElementBitmap, largestSize, largestSize, true);
   364                     shouldComputeColour = true;
   365                 }
   366             }
   367         } catch (Exception unhandled) {
   368             // Handle any exception thrown and return the locks to a sensible state.
   370             // Flag to prevent finally from doubly-unlocking.
   371             Log.e(LOGTAG, "FaviconCache exception!", unhandled);
   372             return null;
   373         } finally {
   374             finishRead();
   375         }
   377         startWrite();
   378         try {
   379             if (shouldComputeColour) {
   380                 // And since we failed, we'll need the dominant colour.
   381                 container.ensureDominantColor();
   382             }
   384             // While the image might not actually BE that size, we set the size field to the target
   385             // because this is the best image you can get for a request of that size using the Favicon
   386             // information provided by this website.
   387             // This way, subsequent requests hit straight away.
   388             FaviconCacheElement newElement = container.addSecondary(newBitmap, targetSize);
   390             if (!wasPermanent) {
   391                 if (setMostRecentlyUsedWithinWrite(newElement)) {
   392                     currentSize.addAndGet(newElement.sizeOf());
   393                 }
   394             }
   395         } finally {
   396             finishWrite();
   397         }
   399         return newBitmap;
   400     }
   402     /**
   403      * Query the cache for the dominant colour stored for the Favicon URL provided, if any.
   404      *
   405      * @param key The URL of the Favicon for which a dominant colour is desired.
   406      * @return The cached dominant colour, or null if none is cached.
   407      */
   408     public int getDominantColor(String key) {
   409         startRead();
   411         try {
   412             FaviconsForURL element = permanentBackingMap.get(key);
   413             if (element == null) {
   414                 element = backingMap.get(key);
   415             }
   417             if (element == null) {
   418                 Log.w(LOGTAG, "Cannot compute dominant color of non-cached favicon. Cache fullness " +
   419                               currentSize.get() + '/' + maxSizeBytes);
   420                 return 0xFFFFFF;
   421             }
   423             return element.ensureDominantColor();
   424         } finally {
   425             finishRead();
   426         }
   427     }
   429     /**
   430      * Remove all payloads stored in the given container from the LRU cache.
   431      * Must be called while holding the write lock.
   432      *
   433      * @param wasRemoved The container to purge from the cache.
   434      */
   435     private void recordRemoved(FaviconsForURL wasRemoved) {
   436         // If there was an existing value, strip it from the insertion-order cache.
   437         if (wasRemoved == null) {
   438             return;
   439         }
   441         int sizeRemoved = 0;
   443         for (FaviconCacheElement e : wasRemoved.favicons) {
   444             sizeRemoved += e.sizeOf();
   445             ordering.remove(e);
   446         }
   448         currentSize.addAndGet(-sizeRemoved);
   449     }
   451     private Bitmap produceCacheableBitmap(Bitmap favicon) {
   452         // Never cache the default Favicon, or the null Favicon.
   453         if (favicon == Favicons.defaultFavicon || favicon == null) {
   454             return null;
   455         }
   457         // Some sites serve up insanely huge Favicons (Seen 512x512 ones...)
   458         // While we want to cache nice big icons, we apply a limit based on screen density for the
   459         // sake of space.
   460         if (favicon.getWidth() > maxCachedWidth) {
   461             return Bitmap.createScaledBitmap(favicon, maxCachedWidth, maxCachedWidth, true);
   462         }
   464         return favicon;
   465     }
   467     /**
   468      * Set an existing element as the most recently used element. Intended for use from read transactions. While
   469      * write transactions may safely use this method, it will perform slightly worse than its unsafe counterpart below.
   470      *
   471      * @param element The element that is to become the most recently used one.
   472      * @return true if this element already existed in the list, false otherwise. (Useful for preventing multiple-insertion.)
   473      */
   474     private boolean setMostRecentlyUsedWithinRead(FaviconCacheElement element) {
   475         reorderingSemaphore.acquireUninterruptibly();
   476         try {
   477             boolean contained = ordering.remove(element);
   478             ordering.offer(element);
   479             return contained;
   480         } finally {
   481             reorderingSemaphore.release();
   482         }
   483     }
   485     /**
   486      * Functionally equivalent to setMostRecentlyUsedWithinRead, but operates without taking the reordering semaphore.
   487      * Only safe for use when called from a write transaction, or there is a risk of concurrent modification.
   488      *
   489      * @param element The element that is to become the most recently used one.
   490      * @return true if this element already existed in the list, false otherwise. (Useful for preventing multiple-insertion.)
   491      */
   492     private boolean setMostRecentlyUsedWithinWrite(FaviconCacheElement element) {
   493         boolean contained = ordering.remove(element);
   494         ordering.offer(element);
   495         return contained;
   496     }
   498     /**
   499      * Add the provided bitmap to the cache as the only available primary for this URL.
   500      * Should never be called with scaled Favicons. The input is assumed to be an unscaled Favicon.
   501      *
   502      * @param faviconURL The URL of the Favicon being stored.
   503      * @param aFavicon The Favicon to store.
   504      */
   505     public void putSingleFavicon(String faviconURL, Bitmap aFavicon) {
   506         Bitmap favicon = produceCacheableBitmap(aFavicon);
   507         if (favicon == null) {
   508             return;
   509         }
   511         // Create a fresh container for the favicons associated with this URL. Allocate extra slots
   512         // in the underlying ArrayList in case multiple secondary favicons are later created.
   513         // Currently set to the number of favicon sizes used in the UI, plus 1, at time of writing.
   514         // Ought to be  tuned as things change for maximal performance.
   515         FaviconsForURL toInsert = new FaviconsForURL(NUM_FAVICON_SIZES);
   517         // Create the cache element for the single element we are inserting, and configure it.
   518         FaviconCacheElement newElement = toInsert.addPrimary(favicon);
   520         startWrite();
   521         try {
   522             // Set the new element as the most recently used one.
   523             setMostRecentlyUsedWithinWrite(newElement);
   525             currentSize.addAndGet(newElement.sizeOf());
   527             // Update the value in the LruCache...
   528             FaviconsForURL wasRemoved;
   529             wasRemoved = backingMap.put(faviconURL, toInsert);
   531             recordRemoved(wasRemoved);
   532         } finally {
   533             finishWrite();
   534         }
   536         cullIfRequired();
   537     }
   539     /**
   540      * Set the collection of primary favicons for the given URL to the provided collection of bitmaps.
   541      *
   542      * @param faviconURL The URL from which the favicons originate.
   543      * @param favicons A List of favicons decoded from this URL.
   544      * @param permanently If true, the added favicons are never subject to eviction.
   545      */
   546     public void putFavicons(String faviconURL, Iterator<Bitmap> favicons, boolean permanently) {
   547         // We don't know how many icons we'll have - let's just take a guess.
   548         FaviconsForURL toInsert = new FaviconsForURL(5 * NUM_FAVICON_SIZES);
   549         int sizeGained = 0;
   551         while (favicons.hasNext()) {
   552             Bitmap favicon = produceCacheableBitmap(favicons.next());
   553             if (favicon == null) {
   554                 continue;
   555             }
   557             FaviconCacheElement newElement = toInsert.addPrimary(favicon);
   558             sizeGained += newElement.sizeOf();
   559         }
   561         startWrite();
   562         try {
   563             if (permanently) {
   564                 permanentBackingMap.put(faviconURL, toInsert);
   565                 return;
   566             }
   568             for (FaviconCacheElement newElement : toInsert.favicons) {
   569                 setMostRecentlyUsedWithinWrite(newElement);
   570             }
   572             // In the event this insertion is being made to a key that already held a value, the subsequent recordRemoved
   573             // call will subtract the size of the old value, preventing double-counting.
   574             currentSize.addAndGet(sizeGained);
   576             // Update the value in the LruCache...
   577             recordRemoved(backingMap.put(faviconURL, toInsert));
   578         } finally {
   579             finishWrite();
   580         }
   582         cullIfRequired();
   583     }
   585     /**
   586      * If cache too large, drop stuff from the cache to get the size back into the acceptable range.
   587      * Otherwise, do nothing.
   588      */
   589     private void cullIfRequired() {
   590         Log.d(LOGTAG, "Favicon cache fullness: " + currentSize.get() + '/' + maxSizeBytes);
   592         if (currentSize.get() <= maxSizeBytes) {
   593             return;
   594         }
   596         startWrite();
   597         try {
   598             while (currentSize.get() > maxSizeBytes) {
   599                 // Cull the least recently used element.
   601                 FaviconCacheElement victim;
   602                 victim = ordering.poll();
   604                 currentSize.addAndGet(-victim.sizeOf());
   605                 victim.onEvictedFromCache();
   607                 Log.d(LOGTAG, "After cull: " + currentSize.get() + '/' + maxSizeBytes);
   608             }
   609         } finally {
   610             finishWrite();
   611         }
   612     }
   614     /**
   615      * Purge all elements from the FaviconCache. Handy if you want to reclaim some memory.
   616      */
   617     public void evictAll() {
   618         startWrite();
   620         // Note that we neither clear, nor track the size of, the permanent map.
   621         try {
   622             currentSize.set(0);
   623             backingMap.clear();
   624             ordering.clear();
   626         } finally {
   627             finishWrite();
   628         }
   629     }
   630 }

mercurial