Image compression and cache efficient loading to avoid oom summary, image compression oom
Efficient loading of large images
Check the maximum available memory for each application:
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); Log.d("TAG", "Max memory is " + maxMemory + "KB");
The BitmapFactory class provides multiple parsing methods (decodeByteArray, decodeFile, decodeResource, etc.) for creating Bitmap objects. We should select an appropriate method based on the image source. For example, the decodeFile method can be used for images on the SD card, the decodeStream method can be used for images on the network, and the decodeResource method can be used for images in resource files.
These methods will try to allocate memory for the constructed bitmap, which will easily lead to OOM. Therefore, each parsing method provides an optional BitmapFactory. the Options parameter. If the inJustDecodeBounds attribute of this parameter is set to true, the resolution method cannot allocate memory to bitmap. The returned value is no longer a Bitmap object, but null. Although Bitmap is null, The outWidth, outHeight, and outMimeType attributes of BitmapFactory. Options are assigned values. This technique allows us to obtain the length and width of the image and the MIME type before loading the image, so as to compress the image as needed. The Code is as follows:
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;
Set BitmapFactory. in Options, the value of inSampleSize can be used to compress images. For example, if we have a 2048*1536 pixel image, set the value of inSampleSize to 4, you can compress the image to 512*384 pixels. Originally, loading this image takes up to 12 Mb of memory. After compression, it only takes up to MB (assume that the image is of the ARGB_8888 type, that is, each pixel occupies 4 bytes ). The following method calculates the appropriate inSampleSize value based on the input width and height:
Public static int calculateInSampleSize (BitmapFactory. options options, int reqWidth, int reqHeight) {// the height and width of the source image final int height = options. outHeight; final int width = options. outWidth; int inSampleSize = 1; if (height> reqHeight | width> reqWidth) {// calculate the final int heightRatio = Math. round (float) height/(float) reqHeight); final int widthRatio = Math. round (float) width/(fl Oat) reqWidth); // The minimum ratio of width to height is used as the value of inSampleSize. This ensures that the width and height of the final image must be greater than or equal to the width and height of the target. InSampleSize = heightRatio <widthRatio? HeightRatio: widthRatio;} return inSampleSize ;}
To use this method, set the inJustDecodeBounds attribute of BitmapFactory. Options to true and parse the image once. Then pass BitmapFactory. Options together with the expected width and height to the calculateInSampleSize method to obtain the appropriate inSampleSize value. Then parse the image again, use the newly obtained inSampleSize value, and set inJustDecodeBounds to false to get the compressed image.
The two parsing codes are as follows:
Public static Bitmap decodeSampledBitmapFromResource (Resources res, int resId, int reqWidth, int reqHeight) {// set inJustDecodeBounds to true for the first resolution to obtain the final BitmapFactory image size. 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 options again. inJustDecodeBounds = false; return BitmapFactory. decodeResource (res, resId, options );}
Simply compress any image into a 100*100 thumbnail, and the call code displayed on the ImageView is as follows:
mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
The above method is suitable for reading an image from an unknown source. because you do not know the size of the image from this unknown source, another method is to use the image that has been loaded into the memory, compress images that have been loaded into the memory and save them to the local disk. This allows you to change an image of 1 MB to a 10 KB image.
The core idea of this method is to first convert the image into an output stream and record the byte array size of the output stream. by calling the compress method of the bitmap object, we compress and format the image, compare the size of the byte array with the target size to obtain the compression ratio, and call the Bitmap scaling method to zoom in and out the computation compression ratio to obtain the compression method.
The Code is as follows:
/*** Image Compression Method: (use the compress method) ** @ explain if the bitmap size is smaller than maxSize, no processing * @ param bitmap * indicates the size of the image to be compressed * @ param maxSize *. Unit: kb */public static void imageZoom (Bitmap bitmap, double maxSize) {// put bitmap in the array to obtain the bitmap size (larger than the original file actually read) ByteArrayOutputStream baos = new ByteArrayOutputStream (); // format, quality, and output stream bitmap. compress (Bitmap. compressFormat. JPEG, 100, baos); byte [] B = baos. toByteArray (); // replace byte with KB double mid = B. length/1024; // obtain how many times the maximum bitmap size is allowed double I = mid/maxSize; // determine whether the space occupied by bitmap is greater than the allowed maximum space. if the space occupied by bitmap is greater than the maximum space, if the size is smaller than the compression value, if (I> 1) is not compressed) {// scale the image here, the square root is used to compress the broadband and height to the square root of the corresponding square root times. // (the width and height remain unchanged, and the size of the largest occupied space is also reached after scaling) bitmap = scale (bitmap, bitmap. getWidth ()/Math. sqrt (I), bitmap. getHeight ()/Math. sqrt (I) ;}}/***** image scaling method ** @ param src *: source image resource * @ param newWidth *: width after scaling * @ param newHeight *: Zoom height */public static Bitmap scale (Bitmap src, double newWidth, double newHeight) {// record the width and height float width of src = src. getWidth (); float height = src. getHeight (); // create a matrix container Matrix matrix = new Matrix (); // calculate the scaling ratio float scaleWidth = (float) newWidth)/width; float scaleHeight = (float) newHeight)/height; // start to scale the matrix. postScale (scaleWidth, scaleHeight); // create a scaled image return Bitmap. createBitmap (src, 0, 0, (int) width, (int) height, matrix, true );}
After compression, You can efficiently load bitmap. This method can effectively avoid oom caused by large charts, but when you need to load a large number of images on the interface, in many cases (such as using ListView, components such as GridView or ViewPager), images displayed on the screen can be continuously increased by sliding the screen and other events, and eventually cause OOM.
This requires caching to avoid oom.
Image Cache
To ensure that the memory usage is always within a reasonable range, images removed from the screen are usually recycled. At this time, the garbage collector will think that you no longer hold the reference of these images, and then perform GC operations on these images. It is very good to solve the problem with this idea. However, to make the program run quickly and load images quickly on the interface, you must consider that after some images are recycled, the user slides it back into the screen. Reload the image you just loaded is undoubtedly a performance bottleneck. You need to find a way to avoid this situation.
At this time, the memory cache technology can be used to solve this problem, which allows components to quickly reload and process images. Next, let's take a look at how to use the memory cache technology to cache images so that your applications can increase response speed and smoothness when loading many images.
The core class here is LruCache (which is provided in the android-support-v4 package ). This class is very suitable for caching images. Its main algorithm principle is to store recently used objects with strong references in javashashmap, in addition, the minimum recently used objects are removed from the memory before the cache value reaches the preset value.
Since Android 2.3 (API Level 9), the garbage collector prefers to recycle objects that hold soft references or weak references, which makes soft references and weak references unreliable.
In addition, in Android 3.0 (API Level 11), image data is stored in the local memory, so it cannot be released in a foreseeable way, this poses a potential risk of application memory overflow and crash.
The following is an example of using LruCache to cache images:
Private LruCache <String, Bitmap> mMemoryCache; @ Override protected void onCreate (Bundle savedInstanceState) {// gets the maximum available memory. exceeding this value will cause an OutOfMemory exception. // LruCache transmits 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) {// rewrite this method to measure the size of each image. By default, the number of images is returned. 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 );}
When you load an image to the ImageView, it is first checked in the cache of LruCache. If the corresponding key value is found, the ImageView is updated immediately. Otherwise, a background thread is enabled 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); } }
BitmapWorkerTask also places the key-value pairs of the newly loaded image into the cache.
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 ;}}
For more information, see link content.