If you are sure about the size you want, scaling the Bitmap yourself will save memory because android won't do it, at least not the way you might expect (see edit at bottom and comments).
I think you need to do some other consideration too. When you decode the bitmap yourself, you're targeting a size, probably the size of the ImageView
(therefore after the size is of the ImageView
has been computed). When inflating the xml that size is not known yet. What happens if the size of the ImageView
changes after you've decoded the Bitmap
? Would you rescale it? In that case wouldn't be more efficient to decode a bigger Bitmap
and then just scale the drawing?
One way to find out if android is scaling the Bitmap
as you would is very simple , try to set a ridiculously big image as src in the xml and see what happens (spoiler alert, it'll blow up).
Digging into the source code, it seems that the Bitmap
for the src of the ImageView
is decoded in this method of Drawable
class, it looks like it's not taking into account the size but only the screen density
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName, BitmapFactory.Options opts) {
if (is == null) {
return null;
}
/* ugh. The decodeStream contract is that we have already allocated
the pad rect, but if the bitmap does not had a ninepatch chunk,
then the pad will be ignored. If we could change this to lazily
alloc/assign the rect, we could avoid the GC churn of making new
Rects only to drop them on the floor.
*/
Rect pad = new Rect();
// Special stuff for compatibility mode: if the target density is not
// the same as the display density, but the resource -is- the same as
// the display density, then don't scale it down to the target density.
// This allows us to load the system's density-correct resources into
// an application in compatibility mode, without scaling those down
// to the compatibility density only to have them scaled back up when
// drawn to the screen.
if (opts == null) opts = new BitmapFactory.Options();
opts.inScreenDensity = Drawable.resolveDensity(res, 0);
Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
if (bm != null) {
byte[] np = bm.getNinePatchChunk();
if (np == null || !NinePatch.isNinePatchChunk(np)) {
np = null;
pad = null;
}
final Rect opticalInsets = new Rect();
bm.getOpticalInsets(opticalInsets);
return drawableFromBitmap(res, bm, np, pad, opticalInsets, srcName);
}
return null;
}
And also, in draw(Canvas canvas)
method of BitmapDrawable
the drawing is done with this line:
canvas.drawBitmap(bitmap, null, mDstRect, paint);
It looks like the Bitmap
is used as it is, it's the destination Rect
that does the scaling
EDIT
Today I also accidentally found out another interesting thing, I put an image in the drawable folder (unspecified density) and retrieved a Bitmap with BitmapFactory.decodeResourceStream(res, value, is, pad, opts)
, same as the one above. The Bitmap I got was much bigger than the image in the drawable folder, I think that if the density is unspecified it will assume mdpi and will even upsample the bitmap for higher target densities.