Android LruCache effectively avoids program OOM

Reference for this article: http://blog.csdn.net/guolin_blog/article/details/9316683
Thank you very much for the author's technical sharing, thank you!

    In the process of android application development, Bitmap OOM is often encountered, and it is also a headache for Android programmers.
The following code checks the maximum available memory of the application.
int maxMemory = (int) (Runtime.getRuntime (). maxMemory () / 1024);
Log.d("TAG", "Max memory is " + maxMemory + "KB");

    When displaying high-resolution images, it is best to compress the images first. The size of the compressed image should be similar to the size of the control used to display it, otherwise it will take up a lot of our precious memory and may have a negative impact on performance. Let's take a look at how to properly compress a large image so that it can be displayed at the optimal size while preventing OOM.
   
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

Setting the property to true can make the parsing method prohibit allocating memory for the bitmap and return null, but the outWidth, outHeight and outMimeType properties of BitmapFactory.Options will all be assigned.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;


Next:
        Estimate the memory required to load the entire image.
        How much memory are you willing to provide in order to load this image.
        The actual size of the control used to display this image.
        The screen size and resolution of the current device.

Image compression can be achieved by setting the value of inSampleSize in BitmapFactory.Options. For example, if we have a picture of 2048*1536 pixels, set the value of inSampleSize to 4, we can compress this picture to 512*384 pixels. Originally, loading this image requires 13M of memory, but after compression, it only takes 0.75M (assuming the image is of ARGB_8888 type, that is, each pixel occupies 4 bytes)

http://qingjuyashi.iteye.com/admin/ blogs/2281061 has a detailed explanation of image compression methods.

Compress the image into a thumbnail of reqWidth * reqHeight pixels:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {
	// The first parsing sets inJustDecodeBounds to true to get the image size
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);
    // Call the method defined above to calculate the inSampleSize value
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    // Use the obtained inSampleSize value to parse the image again
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}


Use image caching technology! ! !

        Memory caching technology provides fast access to images that consume a lot of your application's precious memory. The core class is LruCache (this class is provided in the android-support-v4 package). This class is very suitable for caching images. Its main algorithm principle is to store the most recently used objects in the LinkedHashMap with strong references, and remove the least recently used objects from memory before the cached value reaches a preset value.
In the past, we have often used an implementation of a very popular memory caching technique called SoftReference or WeakReference. But this method is no longer recommended, because starting from Android 2.3 (API Level 9), the garbage collector will be more inclined to recycle objects that hold soft or weak references, which makes soft and weak references become no longer reliable. In addition, in Android 3.0 (API Level 11), the image data is stored in the local memory, so it cannot be released in a predictable way, which has the potential risk of causing the application to overflow memory and crash.

In order to be able to choose an appropriate cache size for LruCache, several factors should be taken into consideration, such as:
       How much memory can your device allocate for each application?
        What is the maximum number of pictures that can be displayed on the device screen at one time? How many images need to be preloaded because there is a chance that they will also be displayed on the screen soon?
        What is the screen size and resolution of your device? An ultra-high-resolution device (such as the Galaxy Nexus) will require more cache space than a lower-resolution device (such as the Nexus S) to hold the same number of images.
        The size and size of the images, and how much memory each image will occupy.
        How often are images accessed? Will some images be accessed more frequently than others? If so, you should probably keep some of the images in memory, or use multiple LruCache objects to differentiate between groups of images.
        Can you maintain a good balance between quantity and quality? Sometimes, it is more efficient to store multiple low-resolution images and load high-resolution images in a thread in the background.


private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
	// Get the maximum value of available memory. Using memory beyond this value will cause an OutOfMemory exception.
	// LruCache passes in the cache value through the constructor, in KB.
	int maxMemory = (int) (Runtime.getRuntime (). maxMemory () / 1024);
	// Use 1/8 of the maximum available memory value as the cache size.
	int cacheSize = maxMemory / 8;
	mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
		@Override
		protected int sizeOf(String key, Bitmap bitmap) {
			// Override this method to measure the size of each image, and return the number of images by default.
			return bitmap.getByteCount() / 1024;
		}
	};
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
	if (getBitmapFromMemCache(key) == null) {
		mMemoryCache.put(key, bitmap);
	}
}

public Bitmap getBitmapFromMemCache(String key) {
	return mMemoryCache.get(key);
}

        One-eighth of the memory allocated to the application by the system is used as the cache size. In a mid-to-high configuration phone, that's about 4MB (32/8) of cache space. A full-screen GridView filled with 4 800x480 resolution images would take up about 1.5 megabytes of space (800*480*4). Therefore, this cache size can store 2.5 pages of images.
When loading an image into ImageView, it will first check in LruCache's cache. If the corresponding key is found, the ImageView is updated immediately, otherwise a background thread is started to load the image.
public void loadBitmap(int resId, ImageView imageView) {
	final String imageKey = String.valueOf(resId);
	final Bitmap bitmap = getBitmapFromMemCache(imageKey);
	if (bitmap != null) {
		imageView.setImageBitmap(bitmap);
	} else {
		imageView.setImageResource(R.drawable.image_placeholder);
		BitmapWorkerTask task = new BitmapWorkerTask(imageView);
		task.execute(resId);
	}
}


Asynchronous thread cache image:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
	// Load the image in the background.
	@Override
	protected Bitmap doInBackground(Integer... params) {
		final Bitmap bitmap = decodeSampledBitmapFromResource(
				getResources(), params[0], 100, 100);
		addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
		return bitmap;
	}
}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326989630&siteId=291194637