Perfect loading of bitamap not only increases user experience, but also avoids memory overflow. Loading bitmap, a careful application will cause crash. to report the following exception.
java.lang.OutofMemoryError: bitmap size exceeds VM budget.
If you need to load the picture is larger. It is easy to cause memory overflow. How to prevent memory overflow?
Read the size and type of bitmap
The BitmapFactory class provides many decoding methods (decodeByteArray(), decodeFile(), decodeResource(), etc.) to load bitmap from different data sources, and to select the appropriate method according to the source of the picture. These methods take up a lot of memory to construct bitmap, so they are easy to cause oom exceptions. How do we avoid OOM exceptions?
Each method has the BitmapFactory.Options class, in which a member variable is inJustDecodeBounds. When we set it to true, loading the image will avoid OOM. After doing so, the bitmap returned is null. But it returns the outWidth, outHeight and outMimeType of the picture. So you wonder, we just want to create a bitmap, and now the returned bitmap is null, what's the use? In fact, this is to get the size and type of the picture before creating the bitmap.
To avoid OOM, before creating a bitmap, let's check its size, unless you're absolutely sure that the app's memory is enough to load the image you want to decode.
Here is the code to get the size and type of the picture:
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;
Load and zoom pictures into memory
Now that we get the size of the image, we can see whether we load the original image directly into memory or zoom in and then load it.
Distance: Now we have a 1024 x 768 pixel image, but ultimately we only need to show a 128 x 96 pixel thumbnail.
Now there is a picture, the original size is 2048x1536. After four times the size of the image is set by inSampleSize, the size of the image is 512x384. It takes 12MB to load the original image (assuming the configuration of the image is ARGB_8888), and 0.75MB to load the scaled image. The following method is to calculate the scaling ratio:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Width and Height of the Original Graph
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
We found that in the above code, the scaling ratio is calculated by multiples of 2. Because when the bitmap is finally constructed, the value is a multiple of 2, and the end is rounded.
Using the above method, first set inJustDecodeBounds to true, get the value of the scaling ratio inSampleSize, then set inJustDecodeBounds to false, and finally use the method in the bitmapFactory class to get the bitmap.
The following is the complete process of getting bitmap.
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Using the above method, if our current requirement is to load a 100x100 thumbnail into ImageView. The code is as follows:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
As you can see, after the above encapsulation, we can easily load any size of the image.