SoFunction
Updated on 2025-03-09

Android image cache Lru algorithm (II)

Preface:
In the previous article, we summarized the processing of Bitmap, and compared the efficiency of various processing and the memory usage.Click to view. We learned that if an application uses a large number of images, it will cause OOM (out of memory). How can we deal with it to reduce the probability of OOM? We have been using SoftReference soft references before. SoftReference is a way that is no longer recommended, because starting from Android 2.3 (API Level 9), the garbage collector will be more inclined to recycle objects holding soft references or weak references, which makes soft references no longer reliable. So today we will learn about a new cache processing algorithm, Lru, and then learn how to implement our image caching based on Lrucache and DiskLruCache.

Lru:
LRU is the abbreviation of Least Recently Used. It is translated as "latest use". LRU cache is implemented using this principle. Simply put, it is to cache a certain amount of data. When the set threshold is exceeded, some expired data will be deleted. For example, we cache 10,000 pieces of data. When the data is less than 10,000, you can add it at will. When it exceeds 10,000, you need to add new data. At the same time, you need to delete the expired data to ensure that we can cache 10,000 pieces of data at the maximum. How to determine which expired data to delete? If you use the LRU algorithm to implement it, you will delete the oldest data.

Implement memory caching based on LruCache:
1.) Initialize MemoryCache
The memory caches Drawable instead of Bitmap. The reason is that Drawable has a great memory advantage over Bitmap.

 int maxMemory = (int) ().maxMemory();//Get the total memory size allocated by the system to the application int mCacheSize = maxMemory / 8;//Set the image memory cache to occupy one-eighth mMemoryCache = new LruCache<String, Drawable>(mCacheSize) {
  //This method must be rewritten to measure the size of the Bitmap  @Override
  protected int sizeOf(String key, Drawable value) {
  if (value instanceof BitmapDrawable) {
   Bitmap bitmap = ((BitmapDrawable) value).getBitmap();
   return bitmap == null ? 0 : ();
  }
  return (key, value);
  }
 };

2.) Add a Drawable to the memory cache 

 /**
  * Add Drawable to memory cache
  *
  * @param key
  * @param drawable
  */
 private void addDrawableToMemoryCache(String key, Drawable drawable) {
 if (getDrawableFromMemCache(key) == null && drawable != null) {
  (key, drawable);
 }
 }

3.) Get a Drawable from the memory cache

 /**
  * Get a Drawable from the memory cache
  *
  * @param key
  * @return
  */
 public Drawable getDrawableFromMemCache(String key) {
 return (key);
 }

4.) Remove a Drawable from the memory cache

 /**
  *Remove from memory cache
  *
  * @param key
  */
 public void removeCacheFromMemory(String key) {
 (key);
 }

5.) Clear the memory cache

 /**
  * Clean the memory cache
  */
 public void cleanMemoryCCache() {
 ();
 } 

In fact, the Lru cache mechanism is essentially stored in a LinkedHashMap storage, in order to ensure the order of inserted data and facilitate cleaning.

Implement disk caching based on DiskLruCache:
The DiskLruCache class is not officially implemented by Google. It needs to be downloaded by yourself. The download address is:/JakeWharton/DiskLruCache

1.) Initialize DiskLruCache 

 File cacheDir = ();//Specify the cache address of the data long diskCacheSize = 1024 * 1024 * 30;//How many bytes of data can be cached at most int appVersion = (context);//Specify the version number of the current application int valueCount = 1;//Specify how many cache files can be corresponding to the same key try {
  mDiskCache = (cacheDir, appVersion, valueCount, diskCacheSize);
 } catch (Exception ex) {
 }

2.) Write a file to disk cache 

 /**
  * Add Bitmap to disk cache
  *
  * @param key
  * @param value
  */
 private void addBitmapToDiskCache(String key, byte[] value) {
 OutputStream out = null;
 try {
   editor = (key);
  if (editor != null) {
  out = (0);
  if (value != null &&  > 0) {
   (value);
   ();
   ();
  } else {
   ();
  }
  }
  ();
 } catch (IOException e) {
  ();
 } finally {
  (out);
 }
 }

3.) Read Drawable from disk cache 

 /**
  * Get a Drawable from disk cache
  *
  * @param key
  * @return
  */
 public Drawable getDrawableFromDiskCache(String key) {
 try {
   snapShot = (key);
  if (snapShot != null) {
  InputStream is = (0);
  Bitmap bitmap = (is);
  Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
  //After reading from disk, add to the memory cache  addDrawableToMemoryCache(key, drawable);
  return drawable;
  }
 } catch (IOException e) {
  ();
 }
 return null;
 }

4.) Remove from disk cache

 /**
  *Remove from disk cache
  *
  * @param key
  */
 public void removeCacheFromDisk(String key) {
 try {
  (key);
 } catch (Exception e) {
 }
 }

5.) Clear the disk cache 

 /**
  * Clean the disk cache
  */
 public void cleanDiskCache() {
 try {
  ();
 } catch (Exception e) {
 }
 }


Image download process:
In the next example, I used a little bit of RxJava knowledge. If you don’t understand RxJava, please understand it yourself.
1.) Use asynchronous method to operate disk cache and network downloads, and the memory cache can be operated in the main thread.

 public void disPlay(final ImageView imageView, String imageUrl) {
  // Generate a unique key  final String key = (imageUrl);
  //Read from memory first  Drawable drawableFromMemCache = getDrawableFromMemCache(key);
  if (drawableFromMemCache != null) {
   (drawableFromMemCache);
   return;
  }
  (imageUrl)
    .map(new Func1<String, Drawable>() {
     @Override
     public Drawable call(String imageUrl) { // Parameter type String      //Read from disk      Drawable drawableFromDiskCache = getDrawableFromDiskCache(key);
      if (drawableFromDiskCache != null) {
       return drawableFromDiskCache;
      }
      //Online download      return download(imageUrl); // Return type Drawable     }
    })
    .subscribeOn(()) // Specify subscribe() to occur in IO thread    .observeOn(()) // Specifies that the callback of Subscriber occurs in the main thread    .subscribe(new Action1<Drawable>() {
     @Override
     public void call(Drawable drawable) { // Parameter type Drawable      (drawable);
     }
    });
 }

2.) Download the image process and processing

 private Drawable download(String imageUrl) {
  HttpURLConnection urlConnection = null;
  ByteArrayOutputStream bos = null;
  InputStream ins = null;
  try {
   final URL url = new URL(imageUrl);
   urlConnection = (HttpURLConnection) ();
   ins = ();
   bos = new ByteArrayOutputStream();
   int b;
   while ((b = ()) != -1) {
    (b);
   }
   ();
   byte[] bytes = ();
   Bitmap bitmap = DiskLruUtils.bytes2Bitmap(bytes);
   String key = (imageUrl);
   Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
   //Add to memory cache   addDrawableToMemoryCache(key, drawable);
   //Add to disk cache   addBitmapToDiskCache(key, bytes);
   return drawable;
  } catch (IOException e) {
   ();
  } finally {
   if (urlConnection != null) {
    ();
   }
   (bos);
   (ins);
  }
  return null;
 }

Attach the final image cache singleton to implement all codes and DiskLruUtils tool class code
 

public class ImageLoadManager {
 private LruCache<String, Drawable> mMemoryCache;//Memory cache private DiskLruCache mDiskCache;//Disk cache private static ImageLoadManager mInstance;//Get image download singleton reference
 /**
   * Constructor
   *
   * @param context
   */
 private ImageLoadManager(Context context) {
  int maxMemory = (int) ().maxMemory();//Get the total memory size allocated by the system to the application  int mCacheSize = maxMemory / 8;//Set the image memory cache to occupy one-eighth  mMemoryCache = new LruCache<String, Drawable>(mCacheSize) {
   //This method must be rewritten to measure the size of the Bitmap   @Override
   protected int sizeOf(String key, Drawable value) {
    if (value instanceof BitmapDrawable) {
     Bitmap bitmap = ((BitmapDrawable) value).getBitmap();
     return bitmap == null ? 0 : ();
    }
    return (key, value);
   }
  };

  File cacheDir = ();//Specify the cache address of the data  long diskCacheSize = 1024 * 1024 * 30;//How many bytes of data can be cached at most  int appVersion = (context);//Specify the version number of the current application  int valueCount = 1;//Specify how many cache files can be corresponding to the same key  try {
   mDiskCache = (cacheDir, appVersion, valueCount, diskCacheSize);
  } catch (Exception ex) {
  }
 }

 /**
   * Get singleton reference
   *
   * @return
   */
 public static ImageLoadManager getInstance(Context context) {
  ImageLoadManager inst = mInstance;
  if (inst == null) {
   synchronized () {
    inst = mInstance;
    if (inst == null) {
     inst = new ImageLoadManager(());
     mInstance = inst;
    }
   }
  }
  return inst;
 }

 public void disPlay(final ImageView imageView, String imageUrl) {
  // Generate a unique key  final String key = (imageUrl);
  //Read from memory first  Drawable drawableFromMemCache = getDrawableFromMemCache(key);
  if (drawableFromMemCache != null) {
   (drawableFromMemCache);
   return;
  }
  (imageUrl)
    .map(new Func1<String, Drawable>() {
     @Override
     public Drawable call(String imageUrl) { // Parameter type String      //Read from disk      Drawable drawableFromDiskCache = getDrawableFromDiskCache(key);
      if (drawableFromDiskCache != null) {
       return drawableFromDiskCache;
      }
      //Online download      return download(imageUrl); // Return type Drawable     }
    })
    .subscribeOn(()) // Specify subscribe() to occur in IO thread    .observeOn(()) // Specifies that the callback of Subscriber occurs in the main thread    .subscribe(new Action1<Drawable>() {
     @Override
     public void call(Drawable drawable) { // Parameter type Drawable      (drawable);
     }
    });
 }


 /**
   * Add Drawable to memory cache
   *
   * @param key
   * @param drawable
   */
 private void addDrawableToMemoryCache(String key, Drawable drawable) {
  if (getDrawableFromMemCache(key) == null && drawable != null) {
   (key, drawable);
  }
 }

 /**
   * Get a Drawable from the memory cache
   *
   * @param key
   * @return
   */
 public Drawable getDrawableFromMemCache(String key) {
  return (key);
 }

 /**
   * Get a Drawable from disk cache
   *
   * @param key
   * @return
   */
 public Drawable getDrawableFromDiskCache(String key) {
  try {
    snapShot = (key);
   if (snapShot != null) {
    InputStream is = (0);
    Bitmap bitmap = (is);
    Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
    //After reading from disk, add to the memory cache    addDrawableToMemoryCache(key, drawable);
    return drawable;
   }
  } catch (IOException e) {
   ();
  }
  return null;
 }

 /**
   * Add Bitmap to disk cache
   *
   * @param key
   * @param value
   */
 private void addBitmapToDiskCache(String key, byte[] value) {
  OutputStream out = null;
  try {
    editor = (key);
   if (editor != null) {
    out = (0);
    if (value != null &&  > 0) {
     (value);
     ();
     ();
    } else {
     ();
    }
   }
   ();
  } catch (IOException e) {
   ();
  } finally {
   (out);
  }
 }

 private Drawable download(String imageUrl) {
  HttpURLConnection urlConnection = null;
  ByteArrayOutputStream bos = null;
  InputStream ins = null;
  try {
   final URL url = new URL(imageUrl);
   urlConnection = (HttpURLConnection) ();
   ins = ();
   bos = new ByteArrayOutputStream();
   int b;
   while ((b = ()) != -1) {
    (b);
   }
   ();
   byte[] bytes = ();
   Bitmap bitmap = DiskLruUtils.bytes2Bitmap(bytes);
   String key = (imageUrl);
   Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
   //Add to memory cache   // addDrawableToMemoryCache(key, drawable);
   //Add to disk cache   addBitmapToDiskCache(key, bytes);
   return drawable;
  } catch (IOException e) {
   ();
  } finally {
   if (urlConnection != null) {
    ();
   }
   (bos);
   (ins);
  }
  return null;
 }

 /**
   *Remove from cache
   *
   * @param key
   */
 public void removeCache(String key) {
  removeCacheFromMemory(key);
  removeCacheFromDisk(key);
 }

 /**
   *Remove from memory cache
   *
   * @param key
   */
 public void removeCacheFromMemory(String key) {
  (key);
 }

 /**
   *Remove from disk cache
   *
   * @param key
   */
 public void removeCacheFromDisk(String key) {
  try {
   (key);
  } catch (Exception e) {
  }
 }

 /**
   * Disk cache size
   *
   * @return
   */
 public long diskCacheSize() {

  return ();
 }

 /**
   * Memory cache size
   *
   * @return
   */
 public long memoryCacheSize() {

  return ();
 }

 /**
   * Close disk cache
   */
 public void closeDiskCache() {
  try {
   ();
  } catch (Exception e) {
  }
 }

 /**
   * Clean the cache
   */
 public void cleanCache() {
  cleanMemoryCCache();
  cleanDiskCache();
 }

 /**
   * Clean the disk cache
   */
 public void cleanDiskCache() {
  try {
   ();
  } catch (Exception e) {
  }
 }

 /**
   * Clean the memory cache
   */
 public void cleanMemoryCCache() {
  ();
 }
}

final class DiskLruUtils {

 /**
   * Close the input and output stream
   */
 public static void closeQuietly(/*Auto*/Closeable closeable) {
  if (closeable != null) {
   try {
    ();
   } catch (RuntimeException rethrown) {
    throw rethrown;
   } catch (Exception ignored) {
   }
  }
 }

 /**
   * Get versionCode
   */
 public static int getAppVersion(Context context) {
  try {
   PackageInfo info = ().getPackageInfo((), 0);
   return ;
  } catch ( e) {
   ();
  }
  return 1;
 }


 public static String hashKeyForDisk(String key) {
  String cacheKey;
  try {
   final MessageDigest mDigest = ("MD5");
   (());
   cacheKey = bytesToHexString(());
  } catch (NoSuchAlgorithmException e) {
   cacheKey = (());
  }
  return cacheKey;
 }

 public static String bytesToHexString(byte[] bytes) {
  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < ; i++) {
   String hex = (0xFF & bytes[i]);
   if (() == 1) {
    ('0');
   }
   (hex);
  }
  return ();
 }

 /**
  * Bitmap → bytes
  */
 public static byte[] bitmap2Bytes(Bitmap bm) {
  if (bm == null) {
   return null;
  }
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  (, 100, baos);
  return ();
 }

 /**
  * bytes → Bitmap
  */
 public static Bitmap bytes2Bitmap(byte[] bytes) {
  return (bytes, 0, );
 }


 /**
  * Drawable → Bitmap
  */
 public static Bitmap drawable2Bitmap(Drawable drawable) {
  if (drawable == null) {
   return null;
  }
  // Take the length and width of the drawable  int w = ();
  int h = ();
  // Take the color format of drawable   config = () !=  ? .ARGB_8888 : .RGB_565;
  // Create a corresponding bitmap  Bitmap bitmap = (w, h, config);
  // Create a canvas corresponding to bitmap  Canvas canvas = new Canvas(bitmap);
  (0, 0, w, h);
  // Draw drawable content into the canvas  (canvas);
  return bitmap;
 }

 /*
   * Bitmap → Drawable
   */
 public static Drawable bitmap2Drawable(Bitmap bm) {
  if (bm == null) {
   return null;
  }
  BitmapDrawable bd = new BitmapDrawable(bm);
  (());
  return new BitmapDrawable(bm);
 }

}

The above is a simple implementation based on Lru image caching. I hope it will be helpful to everyone's learning and I hope everyone will support me more.