Loading and Caching of Android Bitmap

  • 2021-08-28 21:14:50
  • OfStack

Image 1 in Android system is generally represented by Bitmap object, which supports common formats such as png and jpg. Usually, the size of pictures is relatively large, and the memory allowed by a single application is limited, so we need to take some measures to reduce the memory occupation and improve the loading speed.

1. Picture loading

SDK provides the BitmapFactory class for us to load pictures. There are several common methods:

BitmapFactory. decodeFile: Load from file. BitmapFactory. decodeByteArray: Load from byte array. BitmapFactory. decodeStream: Loaded from input stream. BitmapFactory. decodeResource: Load from a resource file.

Suppose we use ImageView to display pictures, which are usually much smaller than the size of pictures, so it is obviously unnecessary to load the whole pictures into memory. In graphics, there is a noun called "down sampling", which reduces the resolution of the image and makes it conform to the size of the display area. With the BitmapFactory. Options class, we can achieve the same function. Here, its inSampleSize parameter is mainly used. If its value is 1, the sampled picture is similar to the original picture 1. If it is 2, the length and width of the sampled picture are 1.5 of the original, and the memory occupied is 1/4 of the original.


public static Bitmap decodeSampleBitmapFromBytes(byte[] data) {
  final BitmapFactory.Options options = new BitmapFactory.Options();
  // inJustDecodeBounds For true Only the original information of the picture is parsed, and the picture is not actually loaded. 
  options.inJustDecodeBounds = true;
  BitmapFactory.decodeByteArray(data, 0, data.length, options);
  //  At this time, the width and height of the picture can be passed through options.outWidth And options.outHeight Get it, we 
  //  You can calculate the sampling ratio according to your own needs. 
  options.inSampleSize = 1;
  // inJustDecodeBounds Set to fales Load the picture into memory. 
  options.inJustDecodeBounds = false;
  return BitmapFactory.decodeResource(res, resId, options);
}

2. Picture cache

Cache is widely used in computer field, such as HTTP cache, DNS cache, etc. Cache can not only improve response speed, but also save server bandwidth, and it is also applicable to picture loading. In the development of Android, 1 generally caches pictures in two levels: memory cache and file cache, and they all have libraries for us to use, which are LruCache and DiskLruCache respectively. It can be seen from the name that both use LRU algorithm, that is, giving priority to those caches that have been used least recently.

2.1. LruCache

LruCache is a cache class provided by Android, which is generally used to manage in-memory cache.


// #1 Determine the cache size. 
int maxMemory = (int)(Runtime.getRuntime().totalMemory() / 1024);
int cacheSize = maxMemory / 8;
// #2 : Rewrite sizeOf Method calculates the memory footprint of each cached object. 
LruCache<String, Bitmap> mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
  @Override
  protected int sizeOf(String key, Bitmap value) {
    return value.getByteCount();
  }
};

LruCache is a generic class that can hold various objects, so it can't calculate the size of stored objects, so we need to override its sizeOf method and do the calculation manually. How is LruCache implemented? In fact, it only encapsulates LinkedHashMap and deals with thread safety. The constructor of LinkedHashMap has a Boolean argument, accessOrder, which is stored in access order when it is true, and in insertion order when it is false. When the elements are stored in the order of access, the elements that are taken out at the tail of them are the most recently used elements, which implements the LRU algorithm. LruCache only needs to calculate the current total cache size every time the put function is called, and remove the element at the end of LinkedHashMap when it exceeds the threshold value.

2.2. DiskLruCache

DiskLruCache and LruCache1 both use LinkedHashMap to implement LRU algorithm, but DiskLruCache is more complicated in implementation and use, after all, it needs to manage files.

Getting the DiskLruCache object requires calling the DiskLruCache. open function:


public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)

It accepts four parameters, the first is the cache directory, the second is the client version number, DiskLruCache thinks that the cache is invalid when the version number changes, the third parameter represents how many files can be associated with each key, and the last parameter specifies the size of the cache. When the object is created, DiskLruCache indexes the cache file in LinkedHashMap based on a log file named "journal" in the buffer directory, where all operations on the buffer are logged. When the buffer size reaches the threshold, the file is cleaned according to LRU algorithm.

Use the DiskLruCache. get function when reading the cache:


public synchronized Snapshot get(String key) throws IOException

The function returns an Snapshot object, through which we can get the input stream of the cache file, and multiple threads can use their respective SnapShot objects to read the cache corresponding to an Key at the same time.

Use the DiskLruCache. edit function when manipulating the cache:


public Editor edit(String key) throws IOException

Once created or changed, commit with the Editor. commit function or cancel with the Editor. abort function. The Snapshot object can still be used to read the contents of the cache corresponding to 1 Key when it is operated, because all operations of Editor will first act on the temporary file. Note that each Key can only fetch one Editor object at the same time, which means that even if Editor does nothing, it should call Editor. abort or Editor. commit function, otherwise the function will return null when fetching again.

2.3, Code Sample


public Bitmap loadBitmap(String url) {
  // DiskLruCache Requires that the key cannot contain special characters, so 
  // 1 Do hashing first. 
  String key = MD5(url);
  Bitmap bitmap = loadBitmapFromMemCache(key);
  if (bitmap != null) {
    return bitmap;
  }
  try {
    bitmap = loadBitmapFromDiskCache(key);
    if (bitmap != null) {
      return bitmap;
    }
    bitmap = loadBitmapFromHttp(url);
    if (bitmap != null) {
      return bitmap;
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
  return null;
}

In the loadBitmapFromHttp function, the picture resource needs to be put into DiskLruCache, and in the loadBitmapFromDiskCache function, the loaded Bitmap object needs to be put into LruCache, thus forming a cache chain.

Summarize


Related articles: