Optimization of Bitmap by Android-Universal-Image-Loader (asynchronously loading cache library of images)

Source: Internet
Author: User

Optimization of Bitmap by Android-Universal-Image-Loader (asynchronously loading cache library of images)
Preface:

The first two articles respectively introduce:

Usage configuration of Android-Universal-Image-Loader (asynchronously loading cache library for images)

Android-Universal-Image-Loader (asynchronous Image loading cache Library) source code

Through the first two articles, we learned about the UIL usage configuration. UIL saves an image on the server to the local machine, loads the image to the memory, and UIL's policy on DiscCache and MemoryCache, but another part is important, because it is a problem that we often need to deal with in daily development:Bitmap optimization.In other words: how to load a large image into the memory and display it. If we do not process it, OOM is very easy to happen.

As a classic image cache framework, UIL will learn how to optimize Bitmap in UIL to avoid OOM, similar problems can be solved in the same way during project development in the future.

Comparison between two methods for loading large images into memory

First, we do not need to use UIL to directly load a large image. What will happen?

Load aaa.jpg of the last 21m's image directly to the memory

 

private String uri_virtual="/mnt/sdcard/UIL/Document/pics/aaa.jpg";Bitmap bm=BitmapFactory.decodeFile(uri_virtual);errImage.setImageBitmap(bm);
Run the program and you will find a crash

 


The following error is reported in logcat:

This is a very common error: Out Of Memory ).

This error is generally caused by loading a file with a size greater than the dalivk heap (usually 16 Mb), or frequent memory usage and untimely release, resulting in insufficient memory.

The OOM solution is to use weak reference WeakReference, manually release the memory System. gc (), and compress Bitmap.

Then we are using UIL to load this large image:

 

image = (ImageView) findViewById(R.id.iv);DisplayImageOptions displayOptions = new DisplayImageOptions.Builder().cacheInMemory(true).bitmapConfig(Bitmap.Config.RGB_565).cacheOnDisk(true).build();ImageLoader.getInstance().displayImage(uri_virtual, image,displayOptions);
Loading successful:

 


It can be seen that UIL processes it internally and makes it loaded successfully.

The loading process of UIL loading optimization analysis has been analyzed in the previous article. Here we start with the run () method of LoadAndDisplayImageTask, because the local file is converted into inputStream in the run () method. Find this method tryLoadBitmap () in the run () method, and there is such a code in the method:
// Check whether the local File contains the cached File imageFile = configuration. diskCache. get (uri); if (imageFile! = Null & imageFile. exists () & imageFile. length ()> 0) {L. d (LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey); loadedFrom = LoadedFrom. DISC_CACHE; checkTaskNotActual (); bitmap = decodeImage (Scheme. FILE. wrap (imageFile. getAbsolutePath ()));}
In line 07, A decodeImage method is executed. Based on the returned value and input parameters, we can easily see that the function of this method is to follow the path of the local image, convert it to bitmap and load it into memory.
private Bitmap decodeImage(String imageUri) throws IOException {ViewScaleType viewScaleType = imageAware.getScaleType();ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,getDownloader(), options);return decoder.decode(decodingInfo);}
From the implementation of the preceding decodeImage method, it is done by decoder. decode (decodingInfo) to convert the local file to bitmap. Then let's look at the decode () method: ImageDecoder is an interface, BaseImageDecoder implements ImageDecoder, and implements the decode method:
/*** Decodes image from URI into {@ link Bitmap }. image is scaled close to incoming {@ linkplain ImageSize target size} * during decoding (depend on incoming parameters ). * @ param decodingInfo Needed data for decoding image: if the actual View does not specify wh as the mobile phone resolution px, otherwise it is the set px value * @ return Decoded bitmap * @ throws IOException if some I/O exception occurs during image reading * @ throws UnsupportedOperationException if image URI has unsupported scheme (protocol) * // @ Overridepublic Bitmap decode (ImageDecodingInfo decodingInfo) throws IOException {Bitmap decodedBitmap; ImageFileInfo imageInfo; InputStream imageStream = getImageStream (decodingInfo); if (imageStream = null) {L. e (ERROR_NO_IMAGE_STREAM, decodingInfo. getImageKey (); return null;} try {imageInfo = defineImageSizeAndRotation (imageStream, decodingInfo); imageStream = resetStream (imageStream, decodingInfo); Options decodingOptions = prepareDecodingOptions (imageInfo. imageSize, decodingInfo); decodedBitmap = BitmapFactory. decodeStream (imageStream, null, decodingOptions);} finally {IoUtils. closeSilently (imageStream);} if (decodedBitmap = null) {L. e (ERROR_CANT_DECODE_IMAGE, decodingInfo. getImageKey ();} else {decodedBitmap = considerExactScaleAndOrientatiton (decodedBitmap, decodingInfo, imageInfo. exif. rotation, imageInfo. exif. flipHorizontal);} return decodedBitmap ;}
Before looking at the method, I would like to explain that ImageDecodingInfo is a very important class, which encapsulates some attributes of the sub-ImageView in our layout, such as android: layout_width android: layout_height and some Options attributes. Options attributes
destOptions.inDensity destOptions.inDither destOptions.inInputShareable destOptions.inJustDecodeBounds destOptions.inPreferredConfig destOptions.inPurgeabledestOptions.inSampleSize destOptions.inScaleddestOptions.inScreenDensitydestOptions.inTargetDensity destOptions.inTempStorage destOptions.inPreferQualityOverSpeeddestOptions.inBitmapdestOptions.inMutable 
Bitmap compression is based on the attributes of bitmap. After introducing ImageDecodingInfo, we will go back to the decode () method above. We can see that 13 rows get InputStream and then 19 rows get the resolution information of the local image according to InputSream, let's take a look at the defineImageSizeAndRotation () method: use Options according to InpuStream. inJustDecodeBounds
/*** // Options. outWidth: 11935options. outHeight: 8554 get the resolution of the Local Image Based on the file stream * @ param imageStream: file stream * @ param decodingInfo: file Information of the local image * @ return * @ throws IOException */protected ImageFileInfo defineImageSizeAndRotation (InputStream imageStream, ImageDecodingInfo decodingInfo) throws IOException {Options options Options = new options (); Options. inJustDecodeBounds = true; BitmapFactory. decodeStream (imageStream, null, options); ExifInfo exif; String imageUri = decodingInfo. getImageUri (); if (decodingInfo. shouldConsiderExifParams () & canDefineExifParams (imageUri, options. outMimeType) {exif = defineExifOrientation (imageUri);} else {exif = new ExifInfo ();} // options. outWidth: 11935options. outHeight: 8554 return new ImageFileInfo (new ImageSize (options. outWidth, options. outHeight, exif. rotation), exif );}
The key is 10 11 12. First, set Options. inJustDecodeBounds = true.
This means: if it is set to true, the decoder will return NULL (no bitmap), but the output... The field is set to allow the caller to query the bitmap without allocating memory for its pixels. During the next 12 lines of execution, only the resolution size will be obtained and bitmap will not be loaded to the memory. After obtaining the size, this method encapsulates the information of the local image and returns the result. We are returning to the decode () method, after 20 21 22, we get the bitmap that eventually meets our needs. Let's take a look at the three rows: imageStream = resetStream (imageStream, decodingInfo ); // The key to getting a file stream again is that 22 rows pass through prepareDecodingOptions () to get the final Options: Calculate the scaling ratio by comparing the local image attributes with the ImagView in the layout:
/*** @ Param imageSize the size of the local image * @ param decodingInfo: the required compilation specifications include wh or the default mobile phone resolution * @ return */protected Options prepareDecodingOptions (ImageSize imageSize, ImageDecodingInfo decodingInfo) {ImageScaleType scaleType = decodingInfo. getImageScaleType (); int scale; if (scaleType = ImageScaleType. NONE) {scale = 1;} else if (scaleType = ImageScaleType. NONE_SAFE) {scale = ImageSizeUtils. computeMinImageSa MpleSize (imageSize);} else {ImageSize targetSize = decodingInfo. getTargetSize (); boolean powerOf2 = scaleType = ImageScaleType. IN_SAMPLE_POWER_OF_2; scale = ImageSizeUtils. computeImageSampleSize (imageSize, targetSize, decodingInfo. getViewScaleType (), powerOf2);} if (scale> 1 & loggingEnabled) {L. d (LOG_SUBSAMPLE_IMAGE, imageSize, imageSize. scaleDown (scale), scale, decodingInfo. getImageKey ();} Optio Ns decodingOptions = decodingInfo. getDecodingOptions (); decodingOptions. inSampleSize = scale; // insampleSize = n indicates that the size of the original bitmap is reduced to 1/n. For example, the capacity occupied by 1/2 is reduced, which is not effective for the generated bitmap, only BitmapFactory // can be used for Bitmap generated by BitmapFactory, such as BitmapFactory. decodeResource (res, id, options. Put options in the parameter. Return decodingOptions ;}
This method only does one thing, decodingOptions. inSampleSize = scale; is to set the value of inSampleSize, Options. the value of inSampleSize is reduced to one of the original vertices. The larger the value, the larger the zooming factor is. You can see the preceding comments. I think getting the value of inSampleSize = scale is the most important part of this article. Next, let's take a closer look at how to get the value of inSampleSize. Let's look at the 16-line execution method computeImageSampleSize ()
Public static int computeImageSampleSize (ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType, boolean powerOf2Scale) {final int srcWidth = srcSize. getWidth (); final int srcHeight = srcSize. getHeight (); final int targetWidth = targetSize. getWidth (); final int targetHeight = targetSize. getHeight (); int scale = 1; switch (viewScaleType) {case FIT_INSIDE: // scale down proportionally or make the image length/width equal to or less than the View length/width if (powerOf2Scale) {final int halfWidth = srcWidth/2; final int halfHeight = srcHeight/2; while (halfWidth/scale)> targetWidth | (halfHeight/scale)> targetHeight) {// | scale * = 2 ;}} else {scale = Math. max (srcWidth/targetWidth, srcHeight/targetHeight); // max} break; case CROP: // scale up the image size to center and make the image length (width) equal to or greater than the View length (width) if (powerOf2Scale) {final int halfWidth = srcWidth/2; final int halfHeight = srcHeight/2; while (halfWidth/scale)> targetWidth & (halfHeight/scale)> targetHeight) {// & scale * = 2 ;}} else {scale = Math. min (srcWidth/targetWidth, srcHeight/targetHeight); // min} break;} if (scale <1) {scale = 1;} scale = considerMaxTextureSize (srcWidth, srcHeight, scale, powerOf2Scale); return scale ;}
Two types are commented out. The large image must be scaled, and the scaling type is 13-14 rows.
while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // ||scale *= 2;}
Divide the Image Width by 1/2 and scale according to the width of the local image, and compare it with the width set in our layout (if the width and height are not set to the resolution of the mobile phone) until it is smaller than the ImageView width we set, obtain the scale value at this time, and then determine the final scale value after 36 rows of considerMaxTextureSize ()
private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) {final int maxWidth = maxBitmapSize.getWidth();final int maxHeight = maxBitmapSize.getHeight();while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) {if (powerOf2) {scale *= 2;} else {scale++;}}return scale;}

This method is used to further determine the scale value. It should be as big as possible. Here we will ignore some of OpenGL ES.
The final scale obtained in the process is set to decodingOptions. inSampleSize = scale.

Then, use the preceding decodedBitmap = BitmapFactory. decodeStream (imageStream, null, decodingOptions); and finally get the suitable Bitmap. The next step is to set the display process, which will not be analyzed here.

The data proves that we will load 21 m images to see how much Bitmap is optimized. In the decode method of BaseImageDecoder, when decodedBitmap = BitmapFactory. decodeStream (imageStream, null, decodingOptions); after execution, we call bitmap. the getByteCount () method obtains the minimum number of bytes required to store the bitmap:
public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {Bitmap decodedBitmap;ImageFileInfo imageInfo;InputStream imageStream = getImageStream(decodingInfo);if (imageStream == null) {L.e(ERROR_NO_IMAGE_STREAM, decodingInfo.getImageKey());return null;}try {imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo);imageStream = resetStream(imageStream, decodingInfo);Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);Log.e("decode"," bytecount: "+decodedBitmap.getByteCount()+"   density:"+decodedBitmap.getDensity()+" H:"+decodedBitmap.getHeight()+"  W:"+decodedBitmap.getWidth());} finally {IoUtils.closeSilently(imageStream);}if (decodedBitmap == null) {L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey());} else {decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation,imageInfo.exif.flipHorizontal);}return decodedBitmap;}
Run our program and we can see the log information 199182b = 194kb. That is to say, the loading of a 21 m image only occupies kb. The effect is quite obvious. Original image resolution w * h = 11935*8554
Simulator bytecount: 199182 density: 160 bitmap's height: 267 bitmap's width: 373 scale: 32 conclusion: What I learned from the three articles: UIL usage configuration, cache Policy and image optimization: by analyzing the UIL source code, we further reviewed the image cache process, deepened our understanding of memoryCache and diskCache, and optimized Bitmap, it also has a clearer understanding.

The first two addresses:

Usage configuration of Android-Universal-Image-Loader (asynchronously loading cache library for images)

Android-Universal-Image-Loader (asynchronous Image loading cache Library) source code


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.