Memory optimization method for images in Android

  • 2020-05-09 19:20:11
  • OfStack

1. Manipulate the image itself

Try not to use setImageBitmap, setImageResource, BitmapFactory.decodeResource to set up a large image, because these methods, after completing decode, end up using createBitmap in the Java layer and consume more memory. Instead, create an bitmap using the BitmapFactory.decodeStream method, and then set it to source of ImageView. The biggest secret of decodeStream is that it directly calls JNI > > nativeDecodeAsset() completes decode, eliminating the need to use createBitmap on Java, thus saving space on Java. If you add the Config parameter of the image when reading, you can reduce the memory load more effectively, thus preventing the memory exception from being thrown more effectively. In addition, decodeStream directly reads the bytecode from the image, so it will not automatically adapt according to the various resolutions of the machine. After using decodeStream, the corresponding image resources need to be configured in hdpi, mdpi and ldpi. Otherwise, the same size (number of pixels) will be displayed on the machine with different resolutions, and the size will not be correct.


InputStream is = this.getResources().openRawResource(R.drawable.pic);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = 2;
Bitmap btp =BitmapFactory.decodeStream(is,null,options);

The above code is to read the thumbnail image named pic under drawable, which is only 1/2 the length and width of the original image. As the image size decreases, so does the memory footprint. The downside of this is that the quality of the images gets worse. The higher the value of inSampleSize, the worse the quality of the images. The quality of the zoom image may be different on different phones due to the different algorithms used by different phone manufacturers.

2. Call the recycle() method of the image


if(!bmp.isRecycle() ){
   bmp.recycle()    // Reclaim the memory used by the image 
   system.gc()      // Remind the system to timely recall 
}

This is not really the way to reduce image memory. The main purpose is to mark the image object and facilitate the recovery of the local data of the image object. The local data of the image object takes up the largest amount of memory and is calculated separately from the memory of the Java part of the program. So it often happens that Java heap is enough to use, while the image is OutOfMemoryError. Calling this method when the image is not in use effectively reduces the peak value of the image local data, thereby reducing the probability of OutOfMemoryError. However, the image object that calls recycle() is in the "defunct" state, causing a program error. So it is not recommended to use this method if there is no guarantee that the image object will never be called again. Note in particular that setImageBitmap(Bitmap) has been used
The image object assigned to the control by the img) method may be called by the system class library, causing a program error.

 

3. Read pictures of local resources in the most provincial way


/**
 *  Read pictures of local resources in the most provincial way 
 */
public static Bitmap readBitMap(Context context, int resId){  
   BitmapFactory.Options opt = new BitmapFactory.Options();  
   opt.inPreferredConfig = Bitmap.Config.RGB_565;  
   opt.inPurgeable = true;  
   opt.inInputShareable = true;  
   //  Get resource images   
   InputStream is = context.getResources().openRawResource(resId);  
   return BitmapFactory.decodeStream(is,null,opt);  
}

There are 4 color modes for loading images in Android, which are: ALPHA_8:1byte memory per pixel, ARGB_4444: 2byte memory per pixel, ARGB_8888: 4byte memory per pixel, RGB_565: 2byte memory per pixel. The default color mode of Android is ARGB_8888. This color mode has the most delicate color and the highest display quality. But again, the memory footprint is the largest. The above code is to read the image resource in RGB_565 (or ARGB_4444) mode. The memory reduction is not as dramatic as in the first method, but for most images, you don't see much difference from the ARGB_8888 mode. However, a color bar may appear when reading an image with a gradient effect. In addition, it will affect the image processing.

 

4. How to change the color mode of an enlarged image using the Matrix object:

Although using the Matrix object to zoom in on an image is bound to consume more memory, it is sometimes necessary to do so. The color mode ARGB_8888 used in the enlarged image is the same even if the original image is the color mode ARGB_4444, and there is no way to directly specify the color mode when zooming in. You can change the image color pattern using the following methods.


Matrix matrix = new Matrix();
float newWidth = 200;       //  The width of the enlarged image 
float newHeight = 300;      //  The length of the enlarged image 
matrix.postScale(newWidth / img.getWidth(), newHeight/ img.getHeight());
Bitmap img1 = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);//  Get a bigger picture 
img2 = img1.copy(Bitmap.Config.ARGB_4444, false);                           //  get  ARGB_4444  Color pattern of the picture 
img = null;
img1 = null;

There is an extra image object img1 generated from the original image. However, the system will actively accept the takeover of img1, so the actual memory is still reduced.

It boils down to the most efficient way to read an image in thumbnail mode and to reduce the memory footprint of each pixel in the image. While both approaches are effective, they have their drawbacks. In practice, it should be applied according to the circumstances. The best way is to avoid garbage objects. For example, in the application of ListView, reuse convertView and so on. If you use AsyncTask to load the image, set the ImageView object to null in time. Because AsyncTask is implemented with a thread pool, the objects referenced in this can have a long lifetime, making GC unreadable. I still believe in Android's memory-receiving takeover mechanism. Although recycle is certainly effective, I always feel that it is not suitable for the principle of Java's memory-receiving takeover.


Related articles: