Reputation: 4683
I get the next exception: Problem decoding into existing bitmap, when setting inBitmap
to true;
Caused by: java.lang.IllegalArgumentException: Problem decoding into existing bitmap
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:460)
...
The interesting thing is that the same code fails in different places when running on:
This is my code which is a copy as it shown in this DevBytes: Bitmap Allocation video.
private BitmapFactory.Options options;
private Bitmap reusedBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView imageView = (ImageView) findViewById(R.id.image_view);
// set the size to option, the images we will load by using this option
options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inMutable = true;
BitmapFactory.decodeResource(getResources(), R.drawable.img1, options);
// we will create empty bitmap by using the option
reusedBitmap = Bitmap.createBitmap(options.outWidth, options.outHeight, Bitmap.Config.ARGB_8888);
// set the option to allocate memory for the bitmap
options.inJustDecodeBounds = false;
options.inSampleSize = 1;
options.inBitmap = reusedBitmap;
// #1
reusedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img1, options);
imageView.setImageBitmap(reusedBitmap);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
options.inBitmap = reusedBitmap;
// #2
reusedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img2, options);
imageView.setImageBitmap(reusedBitmap);
}
});
}
BitmapFactory.decodeResource()
at // #1
BitmapFactory.decodeResource()
at // #2
jpg
and png
. Fails on both.canUseForInBitmap
method, as described here.How to use this inBitmap
property properly?
If you met such problem or you see that I made something stupid, please comment/reply. Any help, will be appreciated. If you know about any workaround, it will be great.
Sorry for not explaining the reason of why I am trying to reuse bitmaps in such way.
The reason for this is GC that locks every time he decides to free memory.
inBitmap
feature should help us to reuse bitmap without allocating new memory which will cause GC to clean the already allocated memory.
For example if I use this common approach:
Log.i("my_tag", "image 1");
imageView.setImageResource(R.drawable.img1);
Log.i("my_tag", "image 2");
imageView.setImageResource(R.drawable.img2);
Log.i("my_tag", "image 3");
imageView.setImageResource(R.drawable.img3);
Then this will be GC work:
I/my_tag ( 5886): image 1
D/dalvikvm( 5886): GC_FOR_ALLOC freed 91K, 2% free 9113K/9240K, paused 15ms, total 15ms
I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.914MB for 11520016-byte allocation
D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20362K/20492K, paused 13ms, total 13ms
I/my_tag ( 5886): image 2
D/dalvikvm( 5886): GC_FOR_ALLOC freed 11252K, 2% free 9111K/9236K, paused 15ms, total 15ms
I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.912MB for 11520016-byte allocation
D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20361K/20488K, paused 35ms, total 35ms
I/my_tag ( 5886): image 3
D/dalvikvm( 5886): GC_FOR_ALLOC freed 11250K, 2% free 9111K/9236K, paused 15ms, total 15ms
I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.913MB for 11520016-byte allocation
D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20361K/20488K, paused 32ms, total 32ms
This is more than 100ms of locked main thread!
The same thing will happen if I will decodeResource()
without the inBitmap
option. So the question is still open, how to use this property ?
Upvotes: 21
Views: 9600
Reputation: 56
I'm not sure about the problem you met with Samsung S3 at #2. But the problem you met with Nexus 4 may be because you put two image in wrong dpi drawable folder. So when it try to decode the bitmap, it can't find the resources.
My phone has screen density is hdpi, at first I try to put two image in drawable-mdpi, and I face the problem with #1. So I changed it to drawable-hdpi, and it worked.
Upvotes: 0
Reputation: 1504
You will need to set options.inMutable to true before #1 & #2 in your code. This may sound a little add, but these are little mines left over that you have stepped upon.
Hope this helps
Upvotes: 1
Reputation: 3535
I tried your code with emulated Nexus 4.
I had default ic_launcher.png file which I copy and pasted it two times in drawable-mdpi (as I usually do). I renamed the two new files to match the names in your code (so that I have less changes to make there).
When I run the application I observed the same as you did.
After few different attempts I decided to copy the the new pngs to other drawable folders - so they were present into:
I run the application and it is working!
I am not really sure why it really works, but obviously it has to be something with the correct screen resolution/density.
Upvotes: 3
Reputation: 2485
Why you are decoding bitmaps ? Just do:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setImageResource(R.drawable.img1);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imageView.setImageResource(R.drawable.img2);
}
});
}
Are you sure that both your images have the same dimension ? According to documentation
If [inBitmap is] set, decode methods that take the Options object will attempt to reuse this bitmap when loading content. If the decode operation cannot use this bitmap, the decode method will return null and will throw an IllegalArgumentException. The current implementation necessitates that the reused bitmap be mutable, and the resulting reused bitmap will continue to remain mutable even when decoding a resource which would normally result in an immutable bitmap.
You should still always use the returned Bitmap of the decode method and not assume that reusing the bitmap worked, due to the constraints outlined above and failure situations that can occur. Checking whether the return value matches the value of the inBitmap set in the Options structure will indicate if the bitmap was reused, but in all cases you should use the Bitmap returned by the decoding function to ensure that you are using the bitmap that was used as the decode destination.
Usage with BitmapFactory
As of KITKAT, any mutable bitmap can be reused by BitmapFactory to decode any other bitmaps as long as the resulting byte count of the decoded bitmap is less than or equal to the allocated byte count of the reused bitmap. This can be because the intrinsic size is smaller, or its size post scaling (for density / sample size) is smaller.
Prior to KITKAT additional constraints apply: The image being decoded (whether as a resource or as a stream) must be in jpeg or png format. Only equal sized bitmaps are supported, with inSampleSize set to 1. Additionally, the configuration of the reused bitmap will override the setting of inPreferredConfig, if set.
EDIT:
If you have large resource bitmap, just load it in asynctask... More info HERE but it can be done much simplier.
Upvotes: 0
Reputation: 36302
It's a little weird that you create a new bitmap and then try to reuse it. Why not just let decodeResource
create a new bitmap in the first place? I suspect your problem in #2 though is that once you've set the ImageView
to use the bitmap, it's not able to reuse it anymore (because it's already in use). The IllegalArgumentException
is mentioned in the docs:
If the decode operation cannot use this bitmap, the decode method will return null and will throw an IllegalArgumentException.
As far as a workaround, you might try to keep two bitmaps around and switch around which one the ImageView
is pointing to, e.g.:
ImageView
to bitmap 1ImageView
to bitmap 2Upvotes: 2