Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to implement disk naming and picture caching algorithm in ImageLoader

2025-01-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly shows you "how to achieve disk naming and picture caching algorithm in ImageLoader", the content is easy to understand, well-organized, hope to help you solve your doubts, the following let Xiaobian lead you to study and learn "how to achieve disk naming and picture caching algorithm in ImageLoader" this article.

one。 Preface

ImageLoader's image cache is divided into disk and memory. This paper analyzes the implementation of disk cache and picture file name algorithm.

It is not stored on disk by default, and the switch needs to be turned on manually.

As follows

DisplayImageOptions options = new DisplayImageOptions.Builder () .cacheInMemory (true) / / default false .cacheOnDisk (true) / / default falseimageLoader.displayImage (", imageView, options, null, null); II. Disk file name / * Generates names for files at disk cache * * @ author Sergey Tarasevich (nostra13 [at] gmail [dot] com) * @ since 1.3.1 * / public interface FileNameGenerator {/ * * Generates unique file name for image defined by URI * / String generate (String imageUri);}

The interface is FileNameGenerator, which is very simple and straightforward, and there is only one method to generate the name of an image file based on the image uri.

It contains two implementation classes

HashCodeFileNameGenerator

Md5FileNameGenerator

Next, look at the implementation of these two classes respectively.

2.1 HashCodeFileNameGenerator/** * Names image file as image URI {@ linkplain String#hashCode () hashcode} * * @ author Sergey Tarasevich (Nostra13 [at] gmail [dot] com) * @ since 1.3.1 * / public class HashCodeFileNameGenerator implements FileNameGenerator {@ Override public String generate (String imageUri) {return String.valueOf (imageUri.hashCode ());}}

The implementation is relatively simple, which can be converted to String according to the hashcode of uri. The default is Hashcode naming.

2.2 Md5FileNameGenerator/** * Names image file as MD5 hash of image URI * * @ author Sergey Tarasevich (nostra13 [at] gmail [dot] com) * @ since 1.4.0 * / public class Md5FileNameGenerator implements FileNameGenerator {private static final String HASH_ALGORITHM = "MD5"; private static final int RADIX = 10 + 26; / / 10 digits + 26 letters @ Override public String generate (String imageUri) {byte [] md5 = getMD5 (imageUri.getBytes ()) BigInteger bi = new BigInteger (md5). Abs (); return bi.toString (RADIX);} private byte [] getMD5 (byte [] data) {byte [] hash = null; try {MessageDigest digest = MessageDigest.getInstance (HASH_ALGORITHM); digest.update (data); hash = digest.digest ();} catch (NoSuchAlgorithmException e) {L.e (e);} return hash }}

Get the byte array through imageUri, and then get the file name through MD5 algorithm.

three。 Disk directory selection

Generally, the sdk/android/data/packageName/cache/uil-images card is preferred by default. If the sdk directory creation fails, the / data/data/packageName directory will be selected.

four。 Picture cache exampl

Among them,-1557665659.0 and 1238391484.0 are the image storage files.

Journal is a descriptive file of operation records, and the contents are as follows

DIRTY: the operation record is created. If there is no CLEAN or REMOVE after the DIRTY, then the image will be deleted.

CLEAN: record successful creation and access

READ: record successful access

REMOVE: record deletion

five。 Disk cache interface

The interface of the disk cache algorithm is DiskCache, which is very simple and straightforward.

Public interface DiskCache {File getDirectory (); File get (String imageUri); boolean save (String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException; boolean save (String imageUri, Bitmap bitmap) throws IOException; boolean remove (String imageUri); void close (); void clear () } method name interpretation getDirectory () get storage directory get (String imageUri) get picture files according to imageUri save (String imageUri, InputStream imageStream, IoUtils.CopyListener listener) save picture remove (String imageUri) delete picture cache close () close disk cache, free resources clear () clean up all disk cache 5.1 implementation classes

Let's take a look at the implementation of each class in detail

six。 LruDiskCachepublic class LruDiskCache implements DiskCache {protected DiskLruCache cache;... Protected final FileNameGenerator fileNameGenerator;... Public LruDiskCache (File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize, int cacheMaxFileCount) throws IOException {... InitCache (cacheDir, reserveCacheDir, cacheMaxSize, cacheMaxFileCount);} private void initCache (File cacheDir, File reserveCacheDir, long cacheMaxSize, int cacheMaxFileCount) throws IOException {try {cache = DiskLruCache.open (cacheDir, 1, 1, cacheMaxSize, cacheMaxFileCount);} catch (IOException e) {.}} @ Override public File getDirectory () {return cache.getDirectory () } @ Override public File get (String imageUri) {DiskLruCache.Snapshot snapshot = null; try {snapshot = cache.get (getKey (imageUri)); return snapshot = = null? Null: snapshot.getFile (0);} catch (IOException e) {L.e (e); return null;} finally {if (snapshot! = null) {snapshot.close () }} @ Override public boolean save (String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {DiskLruCache.Editor editor = cache.edit (getKey (imageUri)); if (editor = = null) {return false;} OutputStream os = new BufferedOutputStream (editor.newOutputStream (0), bufferSize); boolean copied = false Try {copied = IoUtils.copyStream (imageStream, os, listener, bufferSize);} finally {IoUtils.closeSilently (os); if (copied) {editor.commit ();} else {editor.abort ();}} return copied }... @ Override public boolean remove (String imageUri) {try {return cache.remove (getKey (imageUri));} catch (IOException e) {L.e (e); return false;}} @ Override public void close () {try {cache.close () } catch (IOException e) {L.e (e);} cache = null;} @ Override public void clear () {try {cache.delete ();} catch (IOException e) {L.e (e) } try {initCache (cache.getDirectory (), reserveCacheDir, cache.getMaxSize (), cache.getMaxFileCount ());} catch (IOException e) {L.E (e);}} private String getKey (String imageUri) {return fileNameGenerator.generate (imageUri);}}

LruDiskCache has several more important attributes

Protected DiskLruCache cache;protected final FileNameGenerator fileNameGenerator

FileNameGenerator is the file naming generator mentioned above, including hashcode and md5 algorithms. Let's think about it, why do we need FileNameGenerator?

Personally, I think that the uri on the network may be strange, even including special characters, which is obviously not appropriate as a file name. Therefore, it is best to do a hashcode, or md5 conversion, to get the file name at this time.

DiskLruCache, I thought the name was not very good, because it was very similar to LruDiskCache (I saw it as a thing at first glance!)

This DiskLruCache is very important, it maintains the disk image file cache operation records, cache and file correspondence, and so on.

And if you take a closer look at the various methods of LruDiskCache, you will find that they are basically calling the corresponding methods of cache.

So, let's mainly look at the DiskLruCache code.

Final class DiskLruCache implements Closeable {... Private final File directory; private final File journalFile;... Private Writer journalWriter; private final LinkedHashMap lruEntries = new LinkedHashMap (0,0.75f, true);.}

DiskLruCache contains the journalFile, the specific meaning of the file can be the fourth example. Contains

LinkedHashMap lruEntries

Represents the cache record of each picture, String represents key, and Entry represents the description of the picture.

Private final class Entry {private final String key; / * * Lengths of this entry's files. * / private final long [] lengths; / * True if this entry has ever been published. * / private boolean readable; / * * The ongoing edit or null if this entry is not being edited. * / private Editor currentEditor; / * * The sequence number of the most recently committed edit to this entry. * / private long sequenceNumber; public File getCleanFile (int I) {return new File (directory, key + "." + I);} public File getDirtyFile (int I) {return new File (directory, key + "." + I + ".tmp");}}

Let's take saving the picture cache as an example to analyze the workflow of LruDiskCache. First, let's take a look at LruDiskCache's save method.

Public boolean save (String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {DiskLruCache.Editor editor = cache.edit (getKey (imageUri)); if (editor = = null) {return false;} OutputStream os = new BufferedOutputStream (editor.newOutputStream (0), bufferSize); boolean copied = false; try {copied = IoUtils.copyStream (imageStream, os, listener, bufferSize);} finally {IoUtils.closeSilently (os); if (copied) {editor.commit () } else {editor.abort ();}} return copied;} 6.1 getkey (imageUri)

First, the file name, key, is generated from imageUri. At present, we use hashCode.

Private String getKey (String imageUri) {return fileNameGenerator.generate (imageUri);} 6.2cache.editprivate synchronized Editor edit (String key, long expectedSequenceNumber) throws IOException {checkNotClosed (); validateKey (key); Entry entry = lruEntries.get (key); if (expectedSequenceNumber! = ANY_SEQUENCE_NUMBER & & (entry = = null | | entry.sequenceNumber! = expectedSequenceNumber)) {return null; / Snapshot is stale. } if (entry = = null) {entry = new Entry (key); lruEntries.put (key, entry);} else if (entry.currentEditor! = null) {return null; / / Another edit is in progress. } Editor editor = new Editor (entry); entry.currentEditor = editor; / / Flush the journal before creating files to prevent file leaks. JournalWriter.write (DIRTY +'+ key +'\ n'); journalWriter.flush (); return editor;}

Get the corresponding image Entry object from lruEntries according to key. If not, create a new one.

Then write a DIRTY record using journalWriter.

6.3 DiskLruCache opens Dirty picture file stream public OutputStream newOutputStream (int index) throws IOException {synchronized (DiskLruCache.this) {if (entry.currentEditor! = this) {throw new IllegalStateException ();} if (! entry.readable) {written [index] = true;} File dirtyFile = entry.getDirtyFile (index); FileOutputStream outputStream; try {outputStream = new FileOutputStream (dirtyFile) } catch (FileNotFoundException e) {/ / Attempt to recreate the cache directory. Directory.mkdirs (); try {outputStream = new FileOutputStream (dirtyFile);} catch (FileNotFoundException e2) {/ / We are unable to recover. Silently eat the writes. Return NULL_OUTPUT_STREAM;}} return new FaultHidingOutputStream (outputStream);}} public File getDirtyFile (int I) {return new File (directory, key + "." + I + ".tmp");}

Notice that what is opened here is the drity file, which is a normal file followed by a .tmp suffix.

6.4 copyStream writes the network picture stream to the Dirty file public static boolean copyStream (InputStream is, OutputStream os, CopyListener listener, int bufferSize) throws IOException {int current = 0; int total = is.available (); if (total maxSize | | fileCount > maxFileCount | | journalRebuildRequired ()) {executorService.submit (cleanupCallable);}}

Such a file save operation is complete.

seven。 BaseDiskCache

BaseDiskCache is an abstract class that implements basic image file storage, acquisition, deletion and other operations without any restrictions.

Such as save and get, remove, etc.

Public abstract class BaseDiskCache implements DiskCache {... Protected final FileNameGenerator fileNameGenerator;. @ Override public File getDirectory () {return cacheDir;} @ Override public File get (String imageUri) {return getFile (imageUri);} @ Override public boolean save (String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {File imageFile = getFile (imageUri); File tmpFile = new File (imageFile.getAbsolutePath () + TEMP_IMAGE_POSTFIX); boolean loaded = false Try {OutputStream os = new BufferedOutputStream (new FileOutputStream (tmpFile), bufferSize); try {loaded = IoUtils.copyStream (imageStream, os, listener, bufferSize);} finally {IoUtils.closeSilently (os);}} finally {if (loaded & &! tmpFile.renameTo (imageFile)) {loaded = false } if (! loaded) {tmpFile.delete ();}} return loaded;} @ Override public boolean remove (String imageUri) {return getFile (imageUri) .delete ();} @ Override public void close () {/ / Nothing to do} @ Override public void clear () {File [] files = cacheDir.listFiles () If (files! = null) {for (File f: files) {f.delete ();} protected File getFile (String imageUri) {String fileName = fileNameGenerator.generate (imageUri); File dir = cacheDir If (! cacheDir.exists () & &! cacheDir.mkdirs ()) {if (reserveCacheDir! = null & & (reserveCacheDir.exists () | | reserveCacheDir.mkdirs () {dir = reserveCacheDir;}} return new File (dir, fileName);}}

Take save as an example, a tmp file is generated first, and then the network image file stream is written to the tmp file.

OutputStream os = new BufferedOutputStream (new FileOutputStream (tmpFile), loaded = IoUtils.copyStream (imageStream, os, listener, bufferSize)

Then rename the tmp file as an official document

TmpFile.renameTo (imageFile) VIII. UnlimitedDiskCache

Just like BaseDiskCache, there is no new logic.

nine。 LimitedAgeDiskCache

File storage management that limits storage time, when we try to get cache files, we delete files that are too long, and there is no limit to the storage space.

Let's take save and get as examples.

Private final Map loadingDates = Collections.synchronizedMap (new HashMap ()); @ Overridepublic boolean save (String imageUri, Bitmap bitmap) throws IOException {boolean saved = super.save (imageUri, bitmap); rememberUsage (imageUri); return saved;} private void rememberUsage (String imageUri) {File file = getFile (imageUri); long currentTime = System.currentTimeMillis (); file.setLastModified (currentTime); loadingDates.put (file, currentTime);}

When save, the rememberUsage method is called and a HashMap is used to store the cache time.

Get

@ Overridepublic File get (String imageUri) {File file = super.get (imageUri); if (file! = null & & file.exists ()) {boolean cached; Long loadingDate = loadingDates.get (file); if (loadingDate = = null) {cached = false; loadingDate = file.lastModified ();} else {cached = true } if (System.currentTimeMillis ()-loadingDate > maxFileAge) {file.delete (); loadingDates.remove (file);} else if (! cached) {loadingDates.put (file, loadingDate);}} return file;}

Get will be compared according to the current time and cache time, if it is greater than maxFileAge, then delete it, thus achieving a limited time file storage.

These are all the contents of the article "how to implement disk naming and picture caching algorithm in ImageLoader". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report