Android image Bitmap parsing

Posted by billiondevil on Thu, 16 Dec 2021 22:04:06 +0100

1, Introduction

Main uses of Bitmap:
Set background for ImageView
Use as canvas
imageView.setImageBitmap(Bitmap bm); // Set background for ImageView
Canvas canvas = new Canvas(Bitmap bm); // Use as canvas

2, Bitmap compression

Load Bitmap

Bitmap in Android refers to a picture, which can be png format or jpg and other common picture formats. When loading an image, the BitmapFactory class provides four methods:

  • The decodeFile is loaded from the file system
  • The decodeResource loads from the resource and indirectly calls the decodeStream method
  • The decodeStream is loaded from the input stream
  • decodeByteArray loads from a byte array

These four methods are finally implemented at the bottom of Android, corresponding to several native methods of BitmapFactory class.

BitmapFactory.Options

Via bitmapfactory Options enables efficient loading of Bitmap.
Via bitmapfactory Options can load the reduced image at a certain sampling rate and display the reduced image in ImageView, which will reduce the memory occupation, avoid OOM to a certain extent and improve the performance of Bitmap loading.
BitmapFactory supports all four methods of loading pictures provided by BitmapFactory Options parameters, through which you can easily sample and zoom an image.

When the inSampleSize sampling rate is 1, the size of the sampled picture is the original size of the picture; When inSampleSize is greater than 1,
For example, if it is 2, the width / height of the sampled image is 1 / 2 of the size of the original image, the number of pixels is 1 / 4 of the size of the original image, and its memory size is also 1 / 4 of the size of the original image. The value of inSampleSize should always be an exponent of 2.

The process of compressing pictures

public static Bitmap decodeSampledBitmapFromResource(Resources res,int resId,
		 int reqWidth,int reqHeight) {
	 final BitmapFactory.Options options = new BitmapFactory.Options();
	 // 1. Set the inJustDecodeBounds parameter to true and load the picture.
	 options.inJustDecodeBounds = true;
	 BitmapFactory.decodeResource(res,resId,options);
	 // 3. Calculate the sampling rate inSampleSize according to the rules of the sampling rate and the required size of the target View
	 options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
	 // 4. Bitmapfactory Set the inJustDecodeBounds parameter of options to false, and then reload the picture.
	 options.inJustDecodeBounds = false;
	 return BitmapFactory.decodeResource(res,resId,options);
}
	
public static int calculateInSampleSize(
			 BitmapFactory.Options options,int reqWidth,int reqHeight) {
	 // 2. From bitmapfactory Take out the original width and height information of the picture in options.
	 final int height = options.outHeight;
	 final int width = options.outWidth;
	 int inSampleSize = 1;
	 if (height > reqHeight || width > reqWidth) {
		 final int halfHeight = height / 2;
		 final int halfWidth = width / 2;
		 // Calculate the largest inSampleSize value that is a power of 2 and
			keeps both
		 // height and width larger than the requested height and width.
		 while ((halfHeight / inSampleSize) => reqHeight
		&& (halfWidth / inSampleSize) => reqWidth) {
			 inSampleSize *= 2;
		 }
	 }

	 return inSampleSize;
}		


3, Bitmap cache

When an application requests a picture from the network, the program will first obtain it from the memory. If it does not exist in the memory, it will obtain it from the storage device. If it does not exist in the storage device, it will download the picture from the network. Because loading pictures from memory is faster than loading pictures from storage devices, this secondary cache greatly improves the efficiency of the program and saves unnecessary traffic overhead for users. This caching policy applies not only to pictures, but also to other file types.

At present, a commonly used cache algorithm is LRU (Least Recently Used). LRU is the Least Recently Used algorithm. Its core idea is that when the cache is full, it will give priority to those cache objects that are Least Recently Used.
There are two kinds of cache using LRU algorithm: LruCache and DiskLruCache. LruCache is used to implement memory cache, while DiskLruCache acts as storage device cache.

LruCache

LruCache is a generic class. It uses a LinkedHashMap to store external cache objects by strong reference. It provides get and put methods to obtain and add the cache. When the cache is full, LruCache will remove the cache objects used earlier, and then add new cache objects. LruCache is thread safe.

1. Creation of LruCache

int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String,Bitmap>(cacheSize) {
	@Override
	protected int sizeOf(String key,Bitmap bitmap) {
			return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
	}
};

Just provide the total size of the cache and rewrite the sizeOf method. The sizeOf method is used to calculate the size of the cache object, where the size unit needs to be consistent with the total capacity unit. For the above example code, the total capacity is 1 / 8 of the available memory of the current process, in KB, and the sizeOf method completes the size calculation of the Bitmap object. In some special cases, you also need to override the entryRemoved method of LruCache. When LruCache removes the old cache, it will call the entryRemoved method, so you can complete some resource recycling in entryRemoved (if necessary)

2. Cache fetch

mMemoryCache.get(key) //Get a cache object from LruCache

3. Add cache

mMemoryCache.put(key,bitmap) //Add a cache object to LruCache

4. Cache deletion
The remove method removes content from the cache and updates the cache size.

DiskLruCache

DiskLruCache is used to implement storage device cache, that is, disk cache. It realizes the effect of cache by writing cache objects to the file system.

1. Creation of DiskLruCache

DiskLruCache cannot be created by construction method. It provides an open method to create itself.
public static DiskLruCache open(File directory,int appVersion,int valueCount,long maxSize)
The open method has four parameters

  • File directory indicates the storage path of the disk cache in the file system.
  • int appVersion indicates the version number of the application, which is generally set to 1. When the version number changes, DiskLruCache will empty all previous cache files. This feature does not play a significant role in actual development, so it is better to set this parameter to 1.
  • int valueCount indicates the number of data corresponding to a single node, which is generally set to 1.
  • long maxSize indicates the total size of the cache. When the cache size exceeds this setting value, DiskLruCache will clear some caches to ensure that the total size is not greater than this setting value.
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50; //50MB
File diskCacheDir = getDiskCacheDir(mContext,"bitmap");
if (!diskCacheDir.exists()) {
	diskCacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);

2. Add cache for DiskLruCache

The cache adding operation of DiskLruCache is completed through Editor, which represents the editing object of a cache object. Here we still take the image cache as an example. First, we need to get the key corresponding to the image url, and then we can get the Editor object through edit() according to the key. If the cache is being edited, edit() will return null, that is, DiskLruCache does not allow editing of a cache object at the same time. The reason why the url is converted into a key is that there are likely to be special characters in the url of the image, which will affect the direct use of the url in Android. Generally, the md5 value of the url is used as the key.

//Convert url to key
private String hashKeyFormUrl(String url) {
	String cacheKey;
	try {
			final MessageDigest mDigest = MessageDigest.getInstance("MD5");
			mDigest.update(url.getBytes());
			cacheKey = bytesToHexString(mDigest.digest());
	} catch (NoSuchAlgorithmException e) {
			cacheKey = String.valueOf(url.hashCode());
	}
	return cacheKey;
}

private String bytesToHexString(byte[] bytes) {
	StringBuilder sb = new StringBuilder();
	for (int i = 0; i < bytes.length; i++) {
			String hex = Integer.toHexString(0xFF & bytes[i]);
			if (hex.length() == 1) {
					sb.append('0');
			}
			sb.append(hex);
	}
	return sb.toString();
}

After converting the url of the image into a key, you can get the Editor object. For this key, if there are no other Editor objects currently, edit() will return a new Editor object, through which you can get a file output stream. It should be noted that since the open method of DiskLruCache has been set previously, a node can only have one data, so the following disk_ CACHE_ The index constant can be directly set to 0.

String key = hashKeyFormUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
	OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
if (downloadUrlToStream(url,outputStream)) { //When downloading pictures from the network, the pictures can be written to the file system through this file output stream
	editor.commit(); //Commit to write to the file system through the commit() of the Editor
} else {
	editor.abort(); //If an exception occurs during the image download process, use the abort() of the Editor to back out the entire operation
}
mDiskLruCache.flush();
}

3. Cache lookup for DiskLruCache
Get a Snapshot object through the get method of DiskLruCache, and then get the cached file input stream through the Snapshot object. With the file output stream, you can naturally get the Bitmap object. In order to avoid OOM problems caused by loading pictures, it is generally not recommended to load the original pictures directly through bitmapfactory Options object to load a scaled image, but there is a problem with the scaling of FileInputStream in that method. The reason is that FileInputStream is an orderly file stream, and the two calls to decodeStream affect the location attribute of the file stream, resulting in null in the second decodeStream. To solve this problem, you can get the corresponding file descriptor through the file stream, and then through bitmapfactory The decodefiledescriptor method loads a scaled image

Bitmap bitmap = null;
String key = hashKeyFormUrl(url);
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot != null) {
	FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);
	FileDescriptor fileDescriptor = fileInputStream.getFD();
	bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor,
					reqWidth,reqHeight);
	if (bitmap != null) {
			addBitmapToMemoryCache(key,bitmap);
	}
}

In addition to the creation, addition and lookup of the DiskLruCache described above, DiskLruCache also provides methods such as remove and delete for deleting the disk cache.

4, Implementation of ImageLoader

Reference link https://blog.csdn.net/u011521890/article/details/50477725

reference material

Exploration of android development Art

Topics: Android