Overall introduction
As a memory cache, LruCache uses strong reference to cache limited data. When a cached data is accessed, it will be moved to the head of the queue. When a new data is to be added to LruCache and the cache size is to be full, the data at the end of the queue may be collected by the garbage collector (GC). LruCache uses LRU (Least Recently Used) algorithm, that is,:Removes the least recently used data from the queue and allocates memory to the latest incoming data.
- If a piece of data cached in LruCache explicitly needs to be released, it can be overridden
entryRemoved(boolean evicted, K key, V oldValue, V newValue)
- If a piece of data cached by LruCache is not found through the key, you can override it
create(K key)
, This simplifies the calling code, and even if a cached data is missed, it will not return null, but will return data created through create(K key). - If you want to limit the cache size of each piece of data, you can override it
sizeOf(K key, V value)
, as shown below, the maximum cache size of bitmap is 4MB:
int cacheSize = 4 * 1024 * 1024; // 4MiB LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) { protected int sizeOf(String key, Bitmap value) { return (); } }
LruCache
This class is thread-safe. Automatically perform multiple cache operations throughsynchronized
Synchronous cache:
synchronized (cache) { if ((key) == null) { (key, value); } }
LruCache does not allow null as a key or value,get(K)、put(K,V),remove(K)
If the key or value is null, an exception will be thrown:throw new NullPointerException("key == null || value == null")
。
Common APIs
method | Remark |
---|---|
void resize(int maxSize) | Update storage size |
V put(K key, V value) | Save data, return the value corresponding to the previous key, if not, return null |
V get(K key) | Take out the cached data corresponding to the key |
V remove(K key) | Remove the value corresponding to the key |
void evictAll() | Clear cached data |
Map<K, V> snapshot() | Copy a cache and return in order from least recent access to most access sort |
Example of usage
- initialization:
private Bitmap bitmap; private String STRING_KEY = "data_string"; private LruCache<String, Bitmap> lruCache; private static final int CACHE_SIZE = 10 * 1024 * 1024;//10M lruCache = new LruCache<String, Bitmap>(CACHE_SIZE) { @Override protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) { (evicted, key, oldValue, newValue); } @Override protected int sizeOf(String key, Bitmap value) { //The size returned here is expressed in unit kb return () / 1024; } };
The maximum cache is set to 10M, the key is String type, and the value is set to Bitmap
- Save data:
if (lruCache!=null){ (STRING_KEY, bitmap); }
- Get data:
if (lruCache != null) { bitmap = (STRING_KEY); }
- Clear cache:
Source code analysis
Define variables, constructor initialization
// private final LinkedHashMap<K, V> map;//Use LinkedHashMap to store operation data /** Size of this cache in units. Not necessarily the number of elements. */ private int size;//The cache data size is not necessarily equal to the number of elements private int maxSize;//Maximum cache size private int putCount;// Number of times data was added successfully private int createCount;//The number of times cached data is created manually private int evictionCount;//The number of times data was successfully recovered private int hitCount;//The number of hits when searching for data private int missCount;//Number of misses when searching for data /** * @param maxSize for caches that do not override {@link #sizeOf}, this is * the maximum number of entries in the cache. For all other caches, * this is the maximum sum of the sizes of the entries in this cache. */ public LruCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } = maxSize; = new LinkedHashMap<K, V>(0, 0.75f, true); }
Initialized in the constructorLinkedHashMap
,parametermaxSize
Specifies the maximum size of the cache.
Modify cache size
/** * Sets the size of the cache. * * @param maxSize The new maximum size. */ public void resize(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } synchronized (this) { = maxSize; } trimToSize(maxSize); }
resize(int maxSize)
Used to update the size, first add a synchronous lock to updatemaxSize
The value oftrimToSize()
method:
/** * Remove the eldest entries until the total of remaining entries is at or * below the requested size. * * @param maxSize the maximum size of the cache before returning. May be -1 * to evict even 0-sized elements. */ public void trimToSize(int maxSize) { while (true) { K key; V value; synchronized (this) { if (size < 0 || (() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } //If it is already less than the maximum cache, there is no need to continue to execute it if (size <= maxSize) { break; } //Get the least used data recently <K, V> toEvict = (); if (toEvict == null) { break; } key = (); value = (); //Remove this minimally used data from LinkedHashMap (key); //Cached size minus the size of the removed data. If sizeOf is not overridden, the subtracted value is 1 size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } } private int safeSizeOf(K key, V value) { int result = sizeOf(key, value); if (result < 0) { throw new IllegalStateException("Negative size: " + key + "=" + value); } return result; } /** * Returns the size of the entry for {@code key} and {@code value} in * user-defined units. The default implementation returns 1 so that size * is the number of entries and max size is the maximum number of entries. * * <p>An entry's size must not change while it is in the cache. */ protected int sizeOf(K key, V value) { return 1; } protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
trimToSize()
Looping away the least recently used data until the size of the remaining cached data is equal to less than the maximum cache size. Note: We seesizeOf()
andentryRemoved()
Allprotected
to modify, that is, it can be overwritten, ifsizeOf()
Not overwritten, then variablesize
It represents the amount of cached data.maxSize
Represents the maximum number, if overriddensizeOf()
,like:
@Override protected int sizeOf(String key, BitmapDrawable value) { return ().getByteCount() / 1024; }
at this timesize
It represents cached datasize
,maxSize
Represents the maximum cachesize
。
Save data
/** * Caches {@code value} for {@code key}. The value is moved to the head of * the queue. * * @return the previous value mapped by {@code key}. */ public final V put(K key, V value) { //The key or value is empty and throws the exception directly if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); } V previous; synchronized (this) { //The number of data added +1 putCount++; //The size of cached data increases size += safeSizeOf(key, value); //Add cache data. If the value corresponding to the key value is not empty before adding, newValue will overwrite oldValue and return oldValue; //If the value corresponding to the key value is empty, return Null previous = (key, value); if (previous != null) { //The previous oldValue is not empty, then the oldValue is subtracted in the cache size size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, value); } //Recheck cache size trimToSize(maxSize); return previous; }
CallLinkedHashMap
ofput(key, value)
After adding cached data, before adding ifkey
The corresponding valuevalue
Not empty,newValue
Will coveroldValue
and returnoldValue
;ifkey
The corresponding valuevalue
is empty, then returnnull
, then reset the cache according to the return valuesize
and maximum cachemaxSize
size.
Get data
/** * Returns the value for {@code key} if it exists in the cache or can be * created by {@code #create}. If a value was returned, it is moved to the * head of the queue. This returns null if a value is not cached and cannot * be created. */ public final V get(K key) { if (key == null) { throw new NullPointerException("key == null"); } V mapValue; synchronized (this) { //Fetch data through key mapValue = (key); if (mapValue != null) { //If the data is retrieved, the number of hits will be +1 hitCount++; return mapValue; } //No data was retrieved, +1 was missed missCount++; } /* * Attempt to create a value. This may take a long time, and the map * may be different when create() returns. If a conflicting value was * added to the map while create() was working, we leave that value in * the map and release the created value. */ //If create() is not overridden, the null returned by the default create() method V createdValue = create(key); if (createdValue == null) { return null; } //If create() is overridden, that is, the value is manually created based on the key value, continue to execute synchronized (this) { //The number of data creation times +1 createCount++; //Try to add data to cache mapValue = (key, createdValue); if (mapValue != null) { // There was a conflict so undo that last put //mapValue is not empty, which means that the previous key corresponds to data, so it conflicts with the data we created manually. //So perform the undo operation, add mapValue to the cache again, and use mapValue to overwrite createdValue (key, mapValue); } else { //If mapValue is empty, it means that the value corresponding to the previous key value is indeed empty. After we manually add createdValue, //The cache size needs to be recalculated size += safeSizeOf(key, createdValue); } } if (mapValue != null) { entryRemoved(false, key, createdValue, mapValue); return mapValue; } else { trimToSize(maxSize); return createdValue; } } protected V create(K key) { return null; }
The process of fetching data is roughly like this:
- First pass
(key)
Come and try to remove itvalue
,ifvalue
If it exists, it will return directlyvalue
; - if
value
If it does not exist, then the followingcreate(key)
,ifcreate()
Not overwritten, return directlynull
; - if
create()
It is overwritten, that is, it is passedkey
Value created acreatedValue
, then try to passmapValue = (key, createdValue)
BundlecreatedValue
Add to cache,mapValue
yesput()
The return value of the method,mapValue
It representskey
The corresponding value before, ifmapValue
Not empty, it means the previous onekey
The corresponding one has data, so it conflicts with the data we created manually, so perform the undo operation and re-set it.mapValue
Add to cache, usemapValue
Go to covercreatedValue
, and finally recalculate the cache size.
Remove data
/** * Removes the entry for {@code key} if it exists. * * @return the previous value mapped by {@code key}. */ public final V remove(K key) { if (key == null) { throw new NullPointerException("key == null"); } V previous; synchronized (this) { previous = (key); if (previous != null) { size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, null); } return previous; }
Call(key)
passkey
Value deletion is in cachekey
Correspondingvalue
, then recalculate the cache size and return the deletedvalue
。
Some other methods:
//Clear the cache public final void evictAll() { trimToSize(-1); // -1 will evict 0-sized elements } public synchronized final int size() { return size; } public synchronized final int maxSize() { return maxSize; } public synchronized final int hitCount() { return hitCount; } public synchronized final int missCount() { return missCount; } public synchronized final int createCount() { return createCount; } public synchronized final int putCount() { return putCount; } public synchronized final int evictionCount() { return evictionCount; } /** * Returns a copy of the current contents of the cache, ordered from least * recently accessed to most recently accessed. */ //Copy a cache, sorting from the least recent access to the most access in order public synchronized final Map<K, V> snapshot() { return new LinkedHashMap<K, V>(map); }
The above is the detailed content about the use of Android memory cache LruCache and its source code analysis. For more information about the use of Android LruCache, please follow my other related articles!