Ultimate parsing of LruCache and ultimate parsing of LruCache
LruCache is a common and useful data cache tool class provided by android. It stores data through key-value pairs. For LruCache, first look at the original description:
A cache that holds strong references to a limited number of values. each time a value is accessed, it is moved to the head of a queue. when a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection.
LruCache is a cache technology that stores a certain amount of data through strong references. each time a piece of data is used, the data will be moved to the front of the queue (This maintains the order of data access ). when a new data is added to the full cache, the last data in the cache (the least frequently used data) will be cleared.
Through the above description, we can basically understand the functions and implementation methods of LruCache. However, it is difficult to understand the mysteries of LruCache. the red area is marked everywhere. The following describes the red text.
(1) "a certain amount of data": "A certain number"UsuallyThe number of data cached by LruCache, that is, the number of key-value pairs cached by LruCache. note: Here I am talking about "General ". of course, there are other situations: it can also be the size of the memory occupied by the cache data (the unit is generally k, of course, byte). At this time, you need to override the LruCache method: sizeOf (K key, V value) to calculate the memory occupied by each key-value Pair:
Private static LruCache <Integer, Drawable> createIconCache () {final int maxMemory = (int) (Runtime. getRuntime (). maxMemory ()/1024); final int cacheSize = maxMemory/8; return new LruCache <Integer, Drawable> (cacheSize) {protected int sizeOf (Integer key, Drawable drawable) {if (drawable instanceof BitmapDrawable) {// calculate the memory occupied by this BitmapDrawable Bitmap bitmap = (BitmapDrawable) drawable ). getBitmap (); return bitmap. getByteCount ()/1024;} return 1; // otherwise, 1 is returned. By default, 1 is returned }};}
In the above Code, create an LruCache object that caches the image data of Drawable. In the method sizeOf, determine if the cached image data is of the BitmapDrawable type, the returned memory is the size of the image. Otherwise, 1 is returned.
Obviously, when the sizeOf method returns 1, it indicates the number of data cached by the current LruCache is the number of key-value pairs. Let's take a look at how the sizeOf method in LruCache is defined by default:
protected int sizeOf(K key, V value) { return 1; }
By default, 1 is returned. therefore, if your LruCache cache caches data and the image size or memory size of the cached data is limited, You need to override this method sizeOf (K key, V value)
(2) This data will be moved to the front of the queue (This maintains the order of Data Access): in fact, it will soon be known that the LinkedHashMap in hashmap seems to be able to implement this function, of course, LruCache uses LinkedHashMap to see the construction method of LruCache:
Public LruCache (int maxSize) {// maxSize indicates the largest amount of data cached by LruCache if (maxSize <= 0) {throw new IllegalArgumentException ("maxSize <= 0 ");} this. maxSize = maxSize; this. map = new LinkedHashMap <K, V> (0, 0.75f, true); // initialize a LinkedHashMap, and the third parameter is true, this indicates that the LinkedHashMap is sorted by access order .}
(3) Full cache: each time LruCache is used, trimToSize (int maxSize) is called when put (K key, V value) or get (K key) is called) check whether the LruCache is full.
(4) cleared up: The same as above. When trimToSize is called to check whether it is full, it will be cleared if it is full. Check the source code:
Public void trimToSize (int maxSize) {while (true) {// an endless loop: the loop K key; V value; synchronized (this) will not pop out until the current LruCache is not full) {if (size <0 | (map. isEmpty () & size! = 0) {// Error Detection: the number of cached data records cannot be negative, and the number of cached data records cannot be greater than 0 throw new IllegalStateException (getClass () When no data exists (). getName () + ". sizeOf () is reporting inconsistent results! ") ;}If (size <= maxSize) {// if the number of cached data is smaller than the maximum capacity (not full), the loop break is exceeded;} Map. entry <K, V> toEvict = map. eldest (); // use javashashmap to obtain the least commonly used key-value pair data if (toEvict = null) {break;} key = toEvict. getKey (); value = toEvict. getValue (); map. remove (key); // Delete the data size-= <span style = "color: # FF0000;"> safeSizeOf </span> (key, value ); // obtain the size occupied by the deleted key-Value Pair (may be 1, or the memory occupied by the calculated image, or other) evictionCount ++ ;}< span style = "color: # FF0000;"> entryRemoved </span> (true, key, value, null); // call entryRemoved back, it is generally used to notify the client that the data of this key-value pair has been cleared out of LruCache }}
The above description is clear, but the two methods marked in red need to be described below:
<Span style = "color: # FF0000;"> safeSizeOf <span style = "color: #000000;"> let's look at the function prototype: </span>
private int safeSizeOf(K key, V value) { int result = sizeOf(key, value); if (result < 0) { throw new IllegalStateException("Negative size: " + key + "=" + value); } return result; }
You can see that the sizeOf method is actually called.
- EntryRemoved: protected void entryRemoved (boolean evicted, K key, V oldValue, V newValue) {} is an empty function. In fact, this method is rewritten to the subclass, if you want to know that your LruCache can tell you when you quietly delete data, you need to rewrite this method. For example, you create an LruCache that stores bitmap data, if you want to clear the data in LruCache and call the recycle () of the bitmap to determine whether the resource is used, you need to rewrite entryRemoved.
Here, we should have a better understanding of LruCache, but a problem arises. I wonder if you have any idea: Since LruCache will clear the least commonly used key-value pair data, however, it is least commonly used and does not mean it will not be used! If you suddenly use the key-value pair that has been cleared up, do this for the data!
For example, my listview displays 100 menu items. Each menu item displays an image. Each image has a size of 1000 kb. The maximum capacity of my LruCache is only kb, in this way, when I slide the menu, LruCache will clear a lot of images. so what should we do? If I know that the cleared data needs to be used again, I can create a new one.
To solve this problem, the get (K key) method in LruCache checks whether the obtained data is null. If it is null, call the create method to create the data. by default, the create method directly returns null. Therefore, you need to override this method in your own LruCache. let's take a look at the source code of the get (K key) method:
Public final V get (K key) {// obtain the stored data using the key if (key = null) {throw new NullPointerException ("key = null ");} V mapValue; synchronized (this) {mapValue = map. get (key); // if (mapValue! = Null) {hitCount ++; // return mapValue;} missCount ++; // record the number of failed attempts}/** 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. */V createdValue = create (key); // if the current <font color = "# FF0000"> <Font color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <span style = "color: # FF0000; "> <span style =" color: #000000; "> <span style =" color: # FF0000; "> LruCache </span> </font> NO this data, call create to create if (createdValue = null) {return null;} synchronized (this) {createCount ++; // update the number of created mapValue = map. put (ke Y, createdValue); // Add the newly created data to LinkedHashMap if (mapValue! = Null) {// There was a conflict so undo that last put map. put (key, mapValue); // try again if there is a conflict} else {size + = safeSizeOf (key, createdValue ); // update the current <font color = "# FF0000"> <font color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <span style = "color: # FF0000; "> <span style =" color: #000000; "> <span style =" color: # FF0000; "> LruCache </span> </font> </ Font> </font> Number of stored data }}if (mapValue! = Null) {entryRemoved (false, key, createdValue, mapValue); // call entryRemoved to notify that a data return mapValue has been cleared;} else {trimToSize (maxSize ); // check whether return createdValue is full ;}}
Here, I have already made it clear. the LruCache tool class is actually relatively simple. in addition to the features I mentioned above, there are other things that are not very commonly used. Let's take a look at several source methods:
Public final V put (K key, V value) {// forward to <font color = "# FF0000"> <font color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <font color = & quot; # FF0000 & quot;> <font color = "#000000 & quot;> <span style =" color: # FF0000; "> <span style =" color: #000000; "> <span style =" color: # FF0000; "> LruCache </span> </font> </fon T> </font> add data in if (key = null | value = null) {throw new NullPointerException ("key = null | value = null");} V previous; synchronized (this) {putCount ++; // update size + = safeSizeOf (key, value) for the number of data added ); // <font color = "# FF0000"> <font color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <font <font color = "# FF0000"> <font color = "#000000"> <font color = "# FF0000"> <fo Nt color = "#000000"> <font color = "# FF0000"> <font color = "#000000"> <span style = "color: # FF0000; "> <span style =" color: #000000; "> <span style =" color: # FF0000; "> LruCache </span> </font> </font> </font> the number of stored data is updated with previous = map. put (key, value); if (previous! = Null) {size-= safeSizeOf (key, previous) ;}} if (previous! = Null) {entryRemoved (false, key, previous, value);} trimToSize (maxSize); // check whether return previous is full ;}
Public final void evictAll () {// clear all data trimToSize (-1); //-1 will evict 0-sized elements}