Trying to use a recycled bitmap
I get this error and I can't understand why. It only happens sometimes on
some devices.
I have 4 activities: A, B, C & D. They all have an imageview as a
background with the same .jpg and same bitmap. I see this problem in the
user traces, but I can't reproduce it in my Galaxy Nexus. However, I see
it on a virtual device.
I have a class extending Application that generates the bitmap and puts it
in an imageview:
public void setBackground(ImageView img) {
try {
if (bitmapBackground != null && !bitmapBackground
.isRecycled()) {
img.setImageBitmap(bitmapBackground );
} else {
BitmapScaler bitmapScaler= new BitmapScaler
(getResources(),R.drawable.background, WIDTH);
bitmapBackground = bitmapScaler.getBitmap();
img.setImageBitmap(bitmapBackground );
}
} catch (Exception e) {
...
}
}
I open my app in a splash screen (A) and create the bitmap.
Close A, open B and I try to get the bitmap the app uses first, so no need
to create it again.
Open C without closing A (making a .setImageDrawable(null) on background)
and again it uses the bitmap OK in C.
Close C and open D, again it is using the bitmap OK.
Then I push back and return to B, onStart loads the background again,
everything is OK.
I start C again and I get recycled bitmap, why?.
The bitmap which is recycled is in bitmap scaler, although
bitmapBackground is the same. My BitmapScaler:
public class BitmapScaler {
private Bitmap scaled;
private static class Size {
int sample;
float scale;
}
BitmapScaler(Resources resources, int resId, int newWidth) {
if (newWidth > 1200) {
newWidth = 1200;
}
Size size = getRoughSize(resources, resId, newWidth);
roughScaleImage(resources, resId, size);
scaleImage(newWidth);
}
public Bitmap getScaled() {
try {
if (scaled != null && !scaled.isRecycled()) {
return scaled;
}
}
return null;
}
private void scaleImage(int newWidth) {
int width = scaled.getWidth();
int height = scaled.getHeight();
float scaleWidth = ((float) newWidth) / width;
float ratio = ((float) scaled.getWidth()) / newWidth;
int newHeight = (int) (height / ratio);
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
scaled = Bitmap.createBitmap(scaled, 0, 0, width, height, matrix, true);
}
private void roughScaleImage(Resources resources, int resId, Size size) {
Matrix matrix = new Matrix();
matrix.postScale(size.scale, size.scale);
BitmapFactory.Options scaledOpts = new BitmapFactory.Options();
scaledOpts.inSampleSize = size.sample;
scaledOpts.inScaled = false;
scaledOpts.inPreferredConfig = Bitmap.Config.RGB_565;
scaled = BitmapFactory.decodeResource(resources, resId, scaledOpts);
}
private Size getRoughSize(Resources resources, int resId, int newWidth) {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, resId, o);
Size size = getRoughSize(o.outWidth, o.outHeight, newWidth);
return size;
}
public void recycle() {
try {
if (scaled != null && !scaled.isRecycled()) {
scaled.recycle();
scaled = null;
}
} catch (Exception e) {
...
}
}
}
I can't understand why my code doesn't detect that the bitmap was recycled
before it uses it. Is a better solution to use the same background for
many (not all) activities?. Thanks.
No comments:
Post a Comment