I’m trying to optimize and lower memory
footprint of an android app, and increase performance by doing that. If
you want to see what’s been using up your memory you can use Eclipse MAT -
Memory Analyzer Tool. You can plug it in to your Eclipse or use it as a
standalone app, it’s easy to use but also have a lot of advanced
options. I’ll talk about the tool itself some other time, today I’ll
present a case study in which I’ve used it.
Analyzing memory consumption I saw that
ImageDownloader, from internal mini framework, holds up a reference to a
Bitmap object taking 2 840 000 bytes – 2.7MB on the
heap. Instance of this class is an image downloader after all, so it’s
not strange to hold large bitmaps but not the class itself. This class
have several static bitmaps used as placeholder until actual image is
downloaded. It’s clear that placeholder should not be this size so I had
to investigate why this was the case.
Lets say you have a placeholder.png in yours /res/drawable folder, and its size is 500×350 px. When you load this image to a Bitmap object in memory it could take 2 800 000 bytes, ~2.67MB. How is that possible when its only 24 509 bytes ~25KB on a filesystem? What’s with all the diffrence?
First of all, image can not take as
little in memory as on disk, since its compressed on the disc with png
or jpeg compression. This can not be the case in memory and Bitmap has
color and transparency data for each and every pixel. Once we know that,
it’s easy to see how much this image should take in memory: Total pixel
count is 500 x 350, we need 4 bytes per pixel (one for alpha and three colors) so that is 500 x 350 x 4 = 700 000 bytes, ~683KB.
So, image is taking 2 800 000 bytes in memory and it should take 700 000 bytes. we see at once that the difference is exactly 4x. On some other phone it could take 1 400 000 bytes or even an expected 700 000 bytes. This has to do with pixel density of your screen, is your phone ldpi, mdpi, hdpi ili xhdpi configuration. If we know that the phone in question is xhdpi the problem is becoming evident.
The issue here is that the image is in /res/drawable folder. When xhdpi phone needs an image it will look in the /res/drawable-xhdpi folder first, and if it can not find it, eventually it will load from /res/drawable folder but scale it by factor of 2! (ratio ldpi : mdpi : hdpi : xhdpi is 0.75 : 1 : 1.5 : 2 ). Does this explains why our placeholder is holding 4x memory it should? Yes, and perfectly. Image is now actually 1000 x 700px, and thats 1000 x 700 x 4 = 2 800 000 bytes.
By simply moving a image file to /res/drawable-xhdpi bitmap memory consumption will lower to expected 700 000 bytes.
So we fixed this one, but there’s no
need to stop here since obviously we can lower still memory consumption
for a placeholder image. You can split in half memory consumption per
bitmap with one simple trick by asking OS to describe a bitmap not with 4
bytes but with 2. This is not free off course, you’ll have somewhat
lesser quality but for a placeholder we can certainly live with that.
How is this done: BitmapFactory decode… methods take a Options parameter by which you can configure the decoding process. One of the options is inPrefferedConfig that can take a value of Bitmap.Config.ARGB_8888 (one byte for alpha channel and one per color – red, green, blue) but also can take a value of Bitmap.Config.RGB_565 (5 bits, 6 bits and 5 bits respectively for red, green and blue)
Now every pixel will take up 2 bytes so the whole image will take up 500 x 350 x 2 = 350 000 bytes, one half of previous size.
We could go on, this resolution is
probably too high for a placeholder anyway. Or we could convert it to
9patch. This modification will take us to 100KB for this bitmap and
that’s a 28x difference
No comments:
Post a Comment