I have a high resolution image (2588*1603) in drawable folder. If I use below code (1) to set it for the imageView I do not get OOM exception and the image assigned as expected:
public class MainActivity extends ActionBarActivity{
private ImageView mImageView;
int mImageHeight = 0;
int mImageWidth = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.imageView);
mImageView.setScaleType(ScaleType.FIT_CENTER);
BitmapFactory.Options sizeOption = new BitmapFactory.Options();
sizeOption.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.a, sizeOption);
mImageHeight = sizeOption.outHeight;
mImageWidth = sizeOption.outWidth;
mImageView.post(new Runnable() {
@Override
public void run() {
try {
BitmapRegionDecoder bmpDecoder = BitmapRegionDecoder
.newInstance(getResources().openRawResource(R.drawable.a),true);
Rect rect = new Rect(0,0,mImageWidth, mImageHeight);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDensity = getResources().getDisplayMetrics().densityDpi;
Bitmap bmp = bmpDecoder.decodeRegion(rect, options);
mImageView.setImageBitmap(bmp);
} catch (NotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
Note that rect size is exactly the same as image size.
But If I use other methods like for example 2 or 3 I get OOM.
2) mImageView.setBackgroundResource(R.drawable.a);
3) Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.a);
mImageView.setImageBitmap(bmp);
What is the difference between 1 and 2,3 ?
(I know how to solve OOM, I just want to know the difference)
Also, this error may be thrown when there is insufficient native memory to support the loading of a Java class. In a rare instance, a java. lang. OutOfMemoryError may be thrown when an excessive amount of time is being spent doing garbage collection and little memory is being freed.
This is the source of BitmapRegionDecoder#decodeRegion
:
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
checkRecycled("decodeRegion called on recycled region decoder");
if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
|| rect.bottom > getHeight())
throw new IllegalArgumentException("rectangle is not inside the image");
return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top, options);
}
As you can see, it simply calls a native method. I do not understand enough C++ to see whether the method scales the bitmap down (according to your inDensity
flag).
The other two methods use the same native method (nativeDecodeAsset
) to get the bitmap.
Number 2 caches the drawable and thus needs more memory.
After lots of operations (checking if the bitmap is already preloaded or cashed and other things), it calls a native method to get the bitmap. Then, it caches the drawable and sets the background image.
Number 3 is pretty straight forward, it calls a native method after a few operations.
inDensity
flag) and thus needs less memory.I suggest you to debug this problem. In your Manifest, set android:largeHeap="true"
to get more memory. Then, run your 3 different attemps and log the heap size and the bytes allocated by the bitmap.
long maxMemory = Runtime.getRuntime().maxMemory();
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long freeMemory = maxMemory - usedMemory;
long bitmapSize = bmp.getAllocationByteCount();
This will give you a better overview.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With