From design to implementation, we will teach you how to implement Android-Universal-ImageLoader-Cache and androidimageloader step by step.
Indicate the source for reprinting. This article is from the blog of chaossss.
Android-Universal-ImageLoader Github address
Cache
There are two ways to cache images: memory cache and local cache. The difference between the two methods is that the memory cache is cached in the running memory allocated to the application by the Android system. The read speed is fast, but it may cause OOM problems; the local cache is usually cached in the SD card, and the reading speed is slow, but the cache space is sufficient.
So how can we implement the memory cache and local cache? Based on the single responsibility principle, if the abstract of MemoryCache and DiskCache are inconsistent, we need to create the abstract base classes of MemoryCache and DiskCache respectively to implement their respective details. Obviously, the two abstracts are inconsistent, because MemoryCache faces images (Bitmap) and DiskCache faces files ). Therefore, we should implement MemoryCache and DiskCache separately.
Overall Design of MemoryCacheMemoryCache and implementation of related base classes
What should we do now? What about MemoryCache! What application scenarios can we consider for memory cache:
I think that there will certainly be many different situations in Doha. After all, there are endless demands ...... Then, based on the current application scenario, we will begin to design the MemoryCache. Based on the analysis results, we can find that the memory cache has different cache policies and cache restrictions, but has the same abstraction. Therefore, we must first implement the MemoryCache Abstraction:
public interface MemoryCache { boolean put(String key, Bitmap value); Bitmap get(String key); Bitmap remove(String key); Collection<String> keys(); void clear();}
After abstraction is implemented, you have to consider the specific implementation pull. As we have just mentioned, MemoryCache has different cache policies and cache restrictions. Implementation classes with different policies generally do not have inheritance relationships, while MemoryCache with the same restrictions may have abstractions. Then we can get:
public abstract class BaseMemoryCache implements MemoryCache
public abstract class FuzzyKeyMemoryCache implements MemoryCache
public abstract class LruMemoryCache implements MemoryCache
Many may wonder why LruMemoryCache and FuzzyKeyMemoryCache do not inherit from BaseMemoryCache? We may wonder why basemorycache is needed. The reason why we introduced basemorycache is that different memorycaches have the same abstraction. In AUImgLoader, different restrictions are reflected in: the size limit of the cached image and the reference method limit of the cached image.
For more information about references, see strong references, soft references, weak references, and virtual references in Java.
Therefore, in the implementation of BaseMemoryCache, we add the corresponding abstract method:
protected abstract Reference<Bitmap> createReference(Bitmap value);
In contrast to LruMemoryCache, I have explained the principle of LruCache in the in-depth source code analysis. We cache the LruMemoryCache only by using strong references. In other words, the abstraction of LruMemoryCache is different from that of BaseMemory, because basemorycache has a createReference () abstraction method while LruMemoryCache does not need this abstraction method.
FuzzyKeyMemoryCache is a memory cache class that only considers cache policies. It only considers how to process images with the same key. The developer determines how to implement it, because the Processing Methods of FuzzyKeyMemory are completed by calling the abstract interface (I will not release the source code, you can download it by yourself)
BaseMemoryCache
After analysis, we have obtained three basic classes of MemoryCache: BaseMemoryCache, LruMemoryCache, and FuzzyKeyMemoryCache. Next, we will implement the desired details based on BaseMemoryCache.
We have mentioned that BaseMemoryCache is the base class used to handle cache restrictions, and the so-called restrictions are embodied in: size limit and reference restriction. Obviously, only strong references and weak references are worth distinguishing. (Android 2.3 does not encourage developers to use soft references, because both soft reference and weak reference are prone to garbage collection), we can obtain the following two implementation classes:
public abstract class LimitedMemoryCache extends BaseMemoryCache
public class WeakMemoryCache extends BaseMemoryCache
Our final implementation class LimitedMemoryCache came into being ...... In LimitedMemoryCache, we will use a strong reference to cache images. Therefore, what LimitedMemoryCache needs to provide is the image size limit:
public LimitedMemoryCache(int sizeLimit) { this.sizeLimit = sizeLimit; cacheSize = new AtomicInteger(); if (sizeLimit > MAX_NORMAL_CACHE_SIZE) { L.w("You set too large memory cache size (more than %1$d Mb)", MAX_NORMAL_CACHE_SIZE_IN_MB); } }
The rest is to expand the LimitedMemoryCache based on our cache policy.
MemoryCahce tool class
After our efforts, we can cache in the memory, even if the cache implementation class provided by AUImgLoader cannot meet our needs, because the memory cache function module of AUImgLoader adopts the decorator mode architecture, it is very easy to expand existing classes. But now the memory cache module only provides the necessary "memory cache function". Do we need other tools to simplify our use?
You may think about it. We have loaded some small-sized images in the memory. they occupy a small amount of memory and do not need to be cached locally, but we still need to manage them in Fine Granularity (otherwise, when the number of images increases and the memory space is insufficient, processing the images will cause various problems ). Then we need a tool class to help us manage the memory cache. We may wish to create a class called MemoryCacheUtils:
public final class MemoryCacheUtils { private MemoryCacheUtils() { }}
You will notice that this class is a class that cannot be inherited, and instance objects cannot be obtained through the constructor. After all, on the one hand, this tool class does not need to repeatedly create instance objects, but only needs to call the class to execute the method. On the other hand, the tool class does not need to be inherited, and you just need to add it.
Here, MemoryCacheUtils cannot create instance objects, which means that they cannot be created normally. In fact, if you use the Java reflection mechanism, you can still create objects.
So what does this class do for us? After analyzing the actual application scenarios, We can get:
public static String generateKey(String imageUri, ImageSize targetSize){}public static Comparator<String> createFuzzyKeyComparator(){}public static List<Bitmap> findCachedBitmapsForImageUri(String imageUri, MemoryCache memoryCache){}public static List<String> findCacheKeysForImageUri(String imageUri, MemoryCache memoryCache){}public static void removeFromCache(String imageUri, MemoryCache memoryCache){}
I will not talk about the specific implementation here. You can read the source code on your own, and the logic in it is not difficult.
MemoryCache Structure
After the above design and code implementation, we can get the overall structure of the memory cache function module in AUImgLoader:
The createReference () method of BaseMemoryCache was forgotten to be added during the graph creation. Thank you ......
DiskCacheDiskCache overall design and implementation of related base classes
In fact, the design concept of DiskCache is very similar to that of MemoryCache. We only need to follow the new idea to pull it. First, we need to implement the DiskCache Abstraction:
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();}
Then obtain two base classes:
public class LruDiskCache implements DiskCache
public abstract class BaseDiskCache implements DiskCache
Similarly, we can implement the details of BaseDiskCache according to the cache restrictions, so that the local cache function module can be pulled.
Auxiliary Tools for DiskCache
Although the overall design and implementation of DiskCache are quite simple, you still need to consider the problem that every image must be named after it is cached locally, then we should implement the relevant naming classes for it. The naming method for each image is nothing more than hash and MD5. When referencing a graph on the network, we should design a file naming function module for DiskCache:
First, implement the name class Abstraction:
public interface FileNameGenerator { String generate(String imageUri);}
Then we can pull the Implementation Details separately:
public class HashCodeFileNameGenerator implements FileNameGenerator { @Override public String generate(String imageUri) { return String.valueOf(imageUri.hashCode()); }}
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; }}
DiskCache Tool
In addition to the file name generated by the tool class during local cache, DiskCache also needs the tool class to help it do other work: local cache management, local storage policies, etc ...... Therefore, we need to introduce multiple tool classes to help complete these responsibilities.
It should be noted that the tool class and the auxiliary tool class will be in different packages, because the auxiliary tool class is an essential part to complete the duties. Tool classes reduce users' usage costs. The difference between the two makes the packages of the classes inconsistent.
DiskCacheUtils
Like MemoryCacheUtils, DiskCacheUtils is also a class that cannot be inherited and instance objects cannot be created.
public final class DiskCacheUtils { private DiskCacheUtils() { }}
The main application scenarios of this tool class are:
So we can pull the corresponding methods separately:
public static File findInCache(String imageUri, DiskCache diskCache){}public static boolean removeFromCache(String imageUri, DiskCache diskCache){}
StorageUtils
To store images to a local SD card, we need to obtain the directory corresponding to the local cache, so StorageUtils is introduced to complete related matters. Since tool classes cannot inherit and create objects, I will not release the response constructor and class declaration. In StorageUtils, we may use the following scenarios:
To implement the corresponding methods, click OK.
DiskCache Structure
After the above design and code implementation, we can get the overall structure of the memory cache function module in AUImgLoader:
Reflection
You will find that, whether it is DiskCache or MemoryCache, or their tool classes, the code structure is clear and the coupling between classes is low. Many developers will lament that this code is really beautiful. However, I believe that with keen observation, we can see that both MemoryCache and DiskCache use the decorator mode for design, and the function expansion can be "decorated" for the corresponding abstraction; the tool class strictly follows the design principles in the design pattern and should be as independent as possible with different tool modules. Therefore, you should pay attention to these development details during development and constantly refactor the code to make the code structure clearer.
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.