Android Open Source Framework Universal-image-loader full parsing (ii)---picture cache strategy

Source: Internet
Author: User

Reprint please indicate this article from Xiaanming's blog (http://blog.csdn.net/xiaanming/article/details/26810303), please respect others ' hard work results, thank you!

This article continues to introduce universal-image-loader this open-source picture loading framework, describes the picture cache strategy, if you do not understand the use of this open source framework, you can see I wrote an article earlier Android Open source Framework Universal-image-loader full parsing (a)---basic introduction and use, we generally go to load a large number of images, will do cache policy, cache is divided into memory cache and hard disk cache, I have also written a few asynchronous loading a lot of pictures of the article, The memory cache used is LRUCache This class, LRU is least recently used the least recently used algorithm, we can give LRUCache to set a cache image of the maximum value, it will automatically help us manage the total size of the cached image is more than we set the value, Over the deletion of the least recently used pictures, and as a powerful picture loading framework, Universal-image-loader Nature also provides a variety of image caching strategy, below to detailed introduction


Memory Cache


First, let's see what is a strong reference and what is a weak reference?

A strong reference is one that creates an object and assigns it to a reference variable, which is never garbage collected when a reference variable is pointed to it. Even if you prefer to quote oom when you're out of memory and not be recycled by the garbage collector, our new object is a strong reference

Weak references are implemented by the WeakReference class, which has a strong uncertainty, and if the garbage collector scans the object with WeakReference, it will reclaim its memory


Now let's see what memory caching policies Universal-image-loader have

1. Use only strong reference caching

    • Lrumemorycache (This class is the open source framework default memory cache class, the cache is a strong reference to bitmap, below I will analyze this class from the source)

2. A cache with strong references and weak references

    • Usingfreqlimitedmemorycache (if the total number of cached pictures exceeds the limit, first delete the bitmap with the least frequency)
    • Lrulimitedmemorycache (This is also used by the LRU algorithm, and Lrumemorycache is different, he caches the weak references of bitmap)
    • Fifolimitedmemorycache (FIFO-First cache policy, when the set value is exceeded, first delete the bitmap that is first added to the cache)
    • Largestlimitedmemorycache (Delete the largest bitmap object first when exceeding the cache limit)
    • Limitedagememorycache (when the bitmap is added to the cache more than the value we set, delete it)

3. Use only weak reference caching

    • Weakmemorycache (there is no limit to the total size of this type of cache bitmap, the only thing that is not stable, cached images are easily recycled)

All of the memory cache classes provided by Universal-image-loader are described above, and of course we can also use our own memory cache classes, and we'll see how to add these memory caches to our project. We only need to configure Imageloaderconfiguration.memorycache (...) as follows

Imageloaderconfiguration configuration = new Imageloaderconfiguration.builder (this). MemoryCache (New Weakmemorycache ()). build ();

Let's analyze the source code for this class of Lrumemorycache.

Package Com.nostra13.universalimageloader.cache.memory.impl;import Android.graphics.bitmap;import Com.nostra13.universalimageloader.cache.memory.memorycacheaware;import Java.util.collection;import Java.util.hashset;import Java.util.linkedhashmap;import java.util.map;/** * A cache that holds strong references to A Limi Ted Number of Bitmaps. Each time a Bitmap is accessed, it's moved to * the head of a queue. When a Bitmap are added to a full cache, the Bitmap at the end of this queue is evicted and could * become eligible for Garba GE collection.<br/> * <br/> * <b>NOTE:</b> This cache uses only strong references for stored Bit Maps. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.1 */public class Lrumemorycache implements Memoryca Cheaware<string, bitmap> {private final linkedhashmap<string, bitmap> map;private final int maxsize;/** Size of this cache in bytes */private int size;/** @param maxSize Maximum sum of the sizes of the BiTmaps in this cache */public lrumemorycache (int maxSize) {if (maxSize <= 0) {throw new IllegalArgumentException ("Maxsiz E <= 0 ");} This.maxsize = Maxsize;this.map = new linkedhashmap<string, bitmap> (0, 0.75f, true);} /** * Returns the Bitmap for {@code key} if it exists in the cache. If a BITMAP was returned, it's moved to the head * of the queue. This returns null if a Bitmap are not cached. */@Overridepublic final Bitmap get (String key) {if (key = = null) {throw new NullPointerException ("key = = null");} Synchronized (this) {return map.get (key);}} /** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */@Overridepublic Final Boolean put (String key, Bitmap value) {if (key = = NULL | | value = = NULL) {throw new Nullpointerexc Eption ("key = = NULL | | Value = = null ");} Synchronized (this) {size = SizeOf (key, value); Bitmap previous = Map.put (key, value), if (previous! = null) {size = SizeOf (key, previous);}} TrimToSize (maxSize); return true;} /** * Remove THe eldest entries until the total of remaining entries are 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. */private void trimtosize (int maxSize) {while (true) {String key; Bitmap value;synchronized (This) {if (Size < 0 | | (Map.isempty () && size! = 0)) {throw new IllegalStateException (GetClass (). GetName () + ". SizeOf () is reporting inconsistent results!");} if (size <= maxSize | | map.isempty ()) {break;} map.entry<string, bitmap> toevict = Map.entryset (). iterator (). Next (); if (toevict = = null) {break;} Key = Toevict.getkey (); value = Toevict.getvalue (); Map.Remove (key); size = SizeOf (key, value);}} /** removes the entry for {@code key} if it exists. */@Overridepublic final void Remove (String key) {if (key = = null) {throw new NullPointerException ("key = = null");} Synchronized (this) {Bitmap previous = Map.Remove (key); if (previous! = null) {size = SizeOf (key, previous);}} @OverridepubliC collection<string> keys () {synchronized (this) {return new hashset<string> (Map.keyset ());}} @Overridepublic void Clear () {trimtosize ( -1);//-1 would evict 0-sized elements}/** * Returns the size {@code Bitmap} in B Ytes. * <p/> * An entry's size must not change while it's in the cache. */private int sizeOf (String key, Bitmap value) {return value.getrowbytes () * Value.getheight ();} @Overridepublic Synchronized Final String toString () {return String.Format ("lrucache[maxsize=%d]", maxSize);}}
We can see that a linkedhashmap is maintained in this class, and in the Lrumemorycache constructor we can see that we set the maximum value of a cached picture for it maxsize, and instantiate Linkedhashmap, The third parameter from the Linkedhashmap constructor is ture, which indicates that it is sorted in order of access,
Let's take a look at the method of adding Bitmap to Lrumemorycache put (String key, Bitmap value), line 61st, SizeOf () is the number of bytes per picture calculated, and size is the total size of the current cache Bitmap recorded. If the key before the cache bitmap, we need to reduce the previous bitmap, next look at the TrimToSize () method, we look directly at 86 rows, if the current cache of bitmap total is less than the SetPoint maxsize, do not do any processing, If the total number of bitmap currently cached is greater than maxsize, the first element in Linkedhashmap is deleted, and the size of the bitmap corresponds to the byte number

We can see that the cache class is relatively simple, logic is relatively clear, if you want to know the other memory cache logic, you can analyze and analyze its source code, here I simply say fifolimitedmemorycache implementation logic, The class uses the HashMap to cache the weak references of the bitmap, and then uses LinkedList to save the strong references to bitmap that were successfully added to the Fifolimitedmemorycache. If the total number of bitmap added to the fifolimitedmemorycache exceeds the limit, the first element of the LinkedList is deleted, so the FIFO-first cache policy is implemented, and the other caches are similar and interested to see.


Hard disk Cache


Next, we will analyze and analyze the strategy of hard disk cache, which also provides several common caching strategies, but if you don't feel that you are in line with your requirements, you can expand it yourself.

    • Filecountlimiteddisccache (can set the number of cached pictures, when the set value is exceeded, delete the first file added to the hard disk)
    • Limitedagedisccache (the maximum time to set the file to survive, and when this value is exceeded, delete the file)
    • Totalsizelimiteddisccache (Sets the maximum value of the cache bitmap, when the value is exceeded, delete the file that was first added to the hard disk)
    • Unlimiteddisccache (there is no limit to this cache class)

Here we will analyze and analyze the Totalsizelimiteddisccache of the source code implementation

/******************************************************************************* * Copyright 2011-2013 Sergey Tarasevich * * Licensed under the Apache License, Version 2.0 (the "License"); * You are not a use this file except in compliance with the License.  * Obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * unless required by applicable Law or agreed into writing, software * Distributed under the License is distributed on a "as is" BASIS, * without Warra Nties or CONDITIONS of any KIND, either express OR implied. * See the License for the specific language governing permissions and * limitations under the License. /package Com.nostra13.universalimageloader.cache.disc.impl;import Com.nostra13.universalimageloader.cache.disc.limiteddisccache;import Com.nostra13.universalimageloader.cache.disc.naming.filenamegenerator;import Com.nostra13.universalimageloader.core.DefaultConfigurationfactory;import com.nostra13.universalimageloader.utils.l;import java.io.File;/** * Disc Cache Limited by Total cache size. If cache size exceeds specified limit then file with the most oldest last * usage date would be deleted. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see Limiteddisccache * @since 1.0.0 */public class Totalsizel Imiteddisccache extends Limiteddisccache {private static final int min_normal_cache_size_in_mb = 2;private static final in T min_normal_cache_size = min_normal_cache_size_in_mb * 1024x768 * 1024;/** * @param cachedir Directory for file caching. <b>Important:</b> specify separate folder for cached files. It ' s * needed for right cache limit work. * @param maxcachesize Maximum Cache directory Size (in bytes). If cache size exceeds this limit then file with the * Most oldest last usage date would be deleted. */public Totalsizelimiteddisccache (File cachedir, int maxcachesize) {This (CachEdir, Defaultconfigurationfactory.createfilenamegenerator (), maxcachesize);} /** * @param cachedir Directory for file caching. <b>Important:</b> specify separate folder for cached files. It ' s * needed for right cache limit work. * @param filenamegenerator Name generator for cached files * @param maxcachesize Maximum Cache directory size (in BYT ES). If cache size exceeds this limit then file with the * Most oldest last usage date would be deleted . */public Totalsizelimiteddisccache (File cachedir, filenamegenerator filenamegenerator, int maxCacheSize) {super ( Cachedir, Filenamegenerator, maxcachesize); if (Maxcachesize < min_normal_cache_size) {L.W ("You set too small disc Cach E size (less than%1$d Mb) ", MIN_NORMAL_CACHE_SIZE_IN_MB);}} @Overrideprotected int getsize (file file) {return (int) file.length ();}}
This class is inherited Limiteddisccache, in addition to the two constructors, also overrides the GetSize () method, returns the size of the file, and then we'll take a look at Limiteddisccache
/******************************************************************************* * Copyright 2011-2013 Sergey Tarasevich * * Licensed under the Apache License, Version 2.0 (the "License"); * You are not a use this file except in compliance with the License.  * Obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * unless required by applicable Law or agreed into writing, software * Distributed under the License is distributed on a "as is" BASIS, * without Warra Nties or CONDITIONS of any KIND, either express OR implied. * See the License for the specific language governing permissions and * limitations under the License. /package Com.nostra13.universalimageloader.cache.disc;import Com.nostra13.universalimageloader.cache.disc.naming.filenamegenerator;import Com.nostra13.universalimageloader.core.defaultconfigurationfactory;import Java.io.file;import Java.util.collections;impoRT Java.util.hashmap;import Java.util.map;import Java.util.map.entry;import Java.util.set;import java.util.concurrent.atomic.atomicinteger;/** * Abstract Disc Cache limited by some parameter. IF cache exceeds specified limit then file with the most oldest last * usage date would be deleted. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see Basedisccache * @see filenamegenerator * @since 1.0.0 */p  Ublic abstract class Limiteddisccache extends Basedisccache {private static final int invalid_size = -1;//Record cache file Size private  Final Atomicinteger cachesize;//cache file maximum private final int sizelimit;private final map<file, long> lastusagedates = Collections.synchronizedmap (New Hashmap<file, long> ());/** * @param cachedir Directory for File caching. <b>Important:</b> specify separate folder for cached files. It ' s * needed for right cache limit work. * @param sizeLimit Cache limit value. If cache exceeds this limit then file with the most oldest LasT usage date * would be deleted. */public Limiteddisccache (File cachedir, int sizeLimit) {This (Cachedir, Defaultconfigurationfactory.createfilenamegenerator (), sizeLimit);} /** * @param cachedir Directory for file caching. <b>Important:</b> specify separate folder for cached files. It ' s * needed for right cache limit work. * @param filenamegenerator Name generator for cached files * @param sizeLimit Cache limit value. If cache exceeds this limit then file with the most oldest last usage date * would be deleted. */public Limiteddisccache (File cachedir, filenamegenerator filenamegenerator, int sizeLimit) {super (Cachedir, Filenamegenerator); this.sizelimit = Sizelimit;cachesize = new Atomicinteger (); Calculatecachesizeandfillusagemap ();} /** * Another thread calculates the size of the cachedir inside the file and adds the file and the last modified number of milliseconds to the map */private void Calculatecachesizeandfillusagemap () {New Thread (new Runnable () {@Overridepublic void run () {int size= 0; file[] Cachedfiles = Cachedir.listfiles (); if (cachedfiles! = null) {//rarely but it can happen, don ' t know Whyfor (File Cachedfile:cachedfiles) {//getsize () is an abstract method in which subclasses implement the logical size of getsize () + = GetSize (cachedfile);// Add the last modified time of the file to map Lastusagedates.put (Cachedfile, cachedfile.lastmodified ());} Cachesize.set (size);}}). Start ();} /** * Add the file to the map and calculate whether the cache file size exceeds the maximum number of caches we have set * Delete the file that was added first */@Overridepublic void put (String key, file file) {//To add a large small int valuesize = getsize (file);//Gets the total current cache file size int curcachesize = Cachesize.get ();//Determines whether to exceed the set maximum cache value while (Curcachesize + Valuesize > SizeLimit) {int freedsize = Removenext (), if (freedsize = = invalid_size) break,//cache is empty (with Noth ing to delete) Curcachesize = Cachesize.addandget (-freedsize);} Cachesize.addandget (valuesize); Long currenttime = System.currenttimemillis (); file.setlastmodified (currenttime); Lastusagedates.put (file, currenttime);} /** * Generate files from key */@Overridepublic file get (String key) {File File = Super.get (key); Long Currenttime = System.currenttimemillis (); file.setlastmodified (currenttime); lastusagedates.put (file, currenttime); return file;} /** * Hard disk cache cleanup */@Overridepublic void Clear () {lastusagedates.clear (); cachesize.set (0); Super.clear ();} /** * Gets the oldest joined cache file and deletes it */private int removenext () {if (Lastusagedates.isempty ()) {return invalid_size;} Long oldestusage = null; File mostlongusedfile = null; Set<entry<file, long>> entries = Lastusagedates.entryset (); synchronized (lastusagedates) {for (entry< File, long> entry:entries) {if (mostlongusedfile = = null) {Mostlongusedfile = Entry.getkey (); oldestusage = Entry.getV Alue ();} else {Long lastvalueusage = Entry.getvalue (); if (Lastvalueusage < oldestusage) {oldestusage = Lastvalueusage; Mostlongusedfile = Entry.getkey ();}}} int fileSize = 0;if (mostlongusedfile! = null) {if (mostlongusedfile.exists ()) {fileSize = GetSize (mostlongusedfile); if (m Ostlongusedfile.delete ()) {lastusagedates.remove (mostlongusedfile);}} else {Lastusagedates.remove (mostlongusedfile);}} return fileSize;} /** * Abstract method, get file Size * @param @return */protected abstract int getsize (file file);}
In the construction method, line 69th has a method Calculatecachesizeandfillusagemap (), which calculates the file size of the Cachedir and adds the last modified time of the file and file to the map

Then is the method of adding the file to the hard disk cache put (), in 106 rows to determine the total number of cache of the current file and whether the file size to be added to the cache exceeds the cache setting, if the execution Removenext () method is exceeded, then take a look at the specific implementation of this method, 150-167 find the first file to join the hard disk, 169-180 delete it from the file hard disk, and return the size of the file, after the successful deletion of the member variable cachesize need to lose the file size.

Filecountlimiteddisccache This class implementation logic is the same as Totalsizelimiteddisccache, the difference is that the GetSize () method, which returns 1, is expressed as the number of files is 1, which returns the size of the file.

When I finished writing this article, I found Filecountlimiteddisccache and Totalsizelimiteddisccache in the latest source code has been deleted, joined the Lrudisccache, because I was the source of the previous, So I do not change, if you want to know Lrudisccache can go to see the latest source code, I do not introduce here, fortunately, the memory cache has not changed, the following analysis is the latest source of the part, we can not configure the hard disk cache policy in use, Just use the defaultconfigurationfactory.

Let's look at the Creatediskcache () method of this class Defaultconfigurationfactory

/** * Creates Default implementation of {@link DiskCache} depends on incoming parameters */public static DiskCache Creatediskcache (Conte XT context, Filenamegenerator diskcachefilenamegenerator,long diskcachesize, int diskcachefilecount) {File Reservecachedir = Createreservediskcachedir (context); if (diskcachesize > 0 | | diskcachefilecount > 0) {File individ Ualcachedir = storageutils.getindividualcachedirectory (context); Lrudisccache diskcache = new Lrudisccache (Individualcachedir, Diskcachefilenamegenerator, DiskCacheSize, Diskcachefilecount);d Iskcache.setreservecachedir (Reservecachedir); return diskcache;} else {File Cachedir = storageutils.getcachedirectory (context); return new Unlimiteddisccache (Cachedir, Reservecachedir , diskcachefilenamegenerator);}} 
If we configure Diskcachesize and Diskcachefilecount in Imageloaderconfiguration, he uses lrudisccache, otherwise it uses Unlimiteddisccache, In the latest source code also has a hard disk cache class can be configured, that is Limitedagedisccache, can be in the Imageloaderconfiguration.diskcache (...) Configuration

Today to everyone to share here, have not understand the place in the following message, I will try to answer for everyone, the next article I will continue to analyze the framework more deeply, I hope you continue to pay attention!



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.