Android loads large image to prevent OOM

This article is based on the Android development documentation, which mentions heap memory several times. If you are not familiar with the JVM's memory model, you can read this article first.

OOM and SOF

OutOfMemory (OutOfMemory), an OutOfMemoryError thrown when the Java virtual machine cannot allocate objects due to insufficient memory and the garbage collector cannot provide any more memory.

StackOverFlow, a StackOverflowError thrown when an application recurses too deeply causing the stack to overflow.

(PS: The main reason for the memory leak is that the new Object on the Heap cannot be collected by gc, resulting in a memory leak)

Thank you for pointing out the mistakes, here to add two differences

Why do our mobile devices need to deal with loading large images

Has a lot of pictures on the Internet, have hd, have high paste, in PC, we can do whatever you want, but in the mobile terminal, can’t let us so, cell phone memory is not so big, all our photos are loaded into memory in the heap, an app heap allocated memory is limited, we also use other objects store, There is another important factor is that although the wifi is popular, but most of the elderly people don’t know about the concept of flow, use your app for a few minutes not only oom or ANR also owe fee (UI thread loaded bitmap may reduce application performance, leading to the front desk ANR) in response to the slow, so we need to change the size of the picture.

Processing logic

Display in accordance with the actual display specifications, rather than the load of the whole picture, when the user wants to see the full resolution of the picture when the complete picture will be loaded into memory display.

Treatment scheme

The original solution is the one on the official website. Android provides a whole set of methods for bitmap processing. I think this is the basis for other image loading frameworks as well.

CreateScaledBitmap

CreateScaledBitmap is an API in a Bitmap that can be customized to create a new Bitmap. New bitmaps will not be created if they are the same width and height as the original Bitmap. Cons: he must first create a bitmap. That means the image needs to be loaded and decoded first. The performance is not high. So don’t use this mode to load large images unless you want to display the same image in different sizes.

BitmapFactory bitmapFactory = new BitmapFactory();
Bitmap bitmap = null;
try {
    bitmap = BitmapFactory.decodeStream(getAssets().open(("scg.jpeg")));
} catch (IOException e) {
    e.printStackTrace();
}
if(null ! = bitmap) {/ / must pass in a not null bitmap, width, height, whether using bilinear filtering to optimize image bitmap changeBt = bitmap. CreateScaledBitmap (bitmap, 480240,true); 
    imgv.setImageBitmap(bitmap);
    cImgv.setImageBitmap(changeBt);
}
Copy the code

Effect:

inSampleSize

BitmapFactory.Options#inSampleSize

This is a member variable in bitmapFactory. Options: the decoder takes a second sample of the original image and receives an int value, because our bitmap is our pixel map, made up of small pixels one by one (if you zoom in hard enough on an image, you’ll see that it’s made up of a bunch of small squares). Values less than or equal to 1 return the same result as the original image. InSampleSize == 4 returns an image that is 1/4 of the original width/height and 1/16 of the number of pixels. It works by taking one pixel every n cells in the direction of the row and column and combining them into a picture

BitmapFactory.Options cBitmapOptions = new BitmapFactory.Options();
cBitmapOptions.inSampleSize = 2;
Bitmap cbitmap = BitmapFactory.decodeResource(getResources(), R.drawable.scg, cBitmapOptions);

cImgv.setImageBitmap(cbitmap);
Copy the code

Effect:

Bitmapfactory. Options Three pieces (inScaled+inDensity+inTargetDensity)

inScaled

When inScaled is set to true (when this flag is set), if inDensity and inTargetDensity are not 0, the Bitmap will be directly scaled to match inTargetDensity when it is loaded, rather than when it is drawn. (it is already scaled when loaded into heap memory) (this flag is ignored in the.9 figure)

inDensity

Loads the original width of the image, and if this density does not match inTargetDensity, it is scaled to the target density before returning the Bitmap.

inTargetDensity

The display width of the target image, used in conjunction with inScaled and inDensity to determine how to scale it before returning the Bitmap.

The principle of

It does calculations and mixes colors to create new images. So the bigger the image the worse it does. But it displays much better than inSampleSize and is much more color reductive.

Extension (how do I know the original size of the image?)

We need to know the original size of the image and then scale it to the target density, but how do we know the original size of the image? Load into the heap first? And then regenerate into a new Bitmap? Isn’t this almost the same as CreateScaledBitmap? There’s still the risk of getting OOM. InJustDecodeBounds: BitmapFactory.Options, set to true, will parse an image, but will not generate a Bitmap. It will return null, but will render out… Property to allow the caller to query the Bitmap without allocating memory for it. Then we can use the bitmapFactory. Options instance outWidth,outHeight to get the width and height of the original image and compress the pixels.

code

BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
mBitmapOptions.inJustDecodeBounds = true;

BitmapFactory.decodeResource(getResources(), R.drawable.scg, mBitmapOptions);
int srcWidth = mBitmapOptions.outWidth;
int srcHeight = mBitmapOptions.outHeight;

BitmapFactory.Options cBitmapOptions = new BitmapFactory.Options();
cBitmapOptions.inScaled = true;
cBitmapOptions.inDensity = srcWidth;
cBitmapOptions.inTargetDensity = srcWidth * 2;

Bitmap cbitmap = BitmapFactory.decodeResource(getResources(), R.drawable.scg, cBitmapOptions);

cImgv.setImageBitmap(cbitmap);
Toast.makeText(this,""+mBitmapOptions.outHeight,Toast.LENGTH_SHORT).show(); // Result: 265, my local image is 265x265Copy the code

Effect:

comprehensive

Combining the above two solutions, we can use inSampleSize + three pieces. Take a quick initial compression (inSampleSize), then a subtle compression (three-piece set), and use inJustDecodeBounds to measure only the properties of the original image. To get the original image, either download it or just load the corresponding original image into heap memory.

conclusion

The above is the Android official document video bitmap processing method. I think it’s also the compression principle of other frameworks. Glide and Picass have asynchronous decoding and caching. (What I want to say here is that the two frameworks are used almost exactly the same, but it seems that the binding in with is different. They have a different binding life cycle. If you use Glide’s with, if you use this, you might hit Home and go back to your Activity application and crash. If you are interested, you can learn about these two frameworks.)

PS

In the last 🌰 I found: the above schematic diagram written inTargetDensity/inDensity, it is mainly according to the pixel compression, is a kind of graphic processing rules! If the inTargetDensity is 0, the resolution of the image will not change. If the inTargetDensity/inDensity value is very small, it will paste the image. If the inTargetDensity/inDensity value is very high, there will be a noticeable temporary blank screen. If the inTargetDensity/inDensity value is very high, there will be a significant temporary blank screen. If the inTargetDensity/inDensity value is very high, there will be a significant temporary blank screen. This is why inSampleSize is used first when working with large images. To change the width and length of the image, use inDensity. It is scaled according to your phone’s DPI and inDensity. (The XMDPI I use in my simulator is 160 / inDensity, so we can see that the renderer is not half shrunk.) If the inDensity is set to 80 it will be doubled as the original. So 320 is going to be reduced in half. InDensity = srcWidth is equivalent to adaptive.

That is, inTargetDensity can not be set, after setting is according to the inTargetDensity/inDensity value of the image “optimization”; InDensity is set to “zoom” the image according to the pixel density dPI /inDensity of the mobile phone.

Calculation method of bitmap memory size:

Image length x image width x number of bytes per pixel