Android - Out Of Memory - Scaled Bitmaps

I know I am not the only one that finds working with Android Bitmaps not fun to say the least. This is one of the the areas where I would say it is much easier in iOS. Don't take my word for it - search google

Premise

I happen to be working on a client's project - they provide 5 images to scroll through in a simple view pager. It is a simple onboarding flow - you see it in tons of app. Signup and you get greeted with 4-5 screens explaining why this app is the best thing since sliced bread. I think to myself a cinch - this is easy.

I grab the images provided by the graphic designer, load them and start swiping away.

It works for the first run - but subsequent runs start crashing the app with the dreaded Out of Memory exception. What gives? these files are really not that large (340x604)- 282 kb. Another odd thing the crashes seem to occur more frequently on the newer higher-end devices..hmm what is going on.

So after a few hours of tinkering and scouring tons of stackoverflow posts and not readily finding a suitable answer - I download a sample from Bret Duncavage's Github - TangoAndCache (highly recommended and something I need to post on in the future).

No crashes - and he has way more images being loaded. So what am I doing wrong.

I decided to step through his code and see what was going on. In his sample he was downloading images directly from a web server, loading the byte array into a bitmap and then displaying the bitmap - nothing special. Surely loading from the web has to be slower performing than my local files stored in the drawable folder.

Then it hits me - the error says "Out of Memory" - how much memory is my image using up. Could it really be more memory than what Brett is loading. Doubtful but I go along with my thought process and set a breakpoint when loading one of my images.

Eureka - or more like this really reeks of something gone really bad. It shows 1 of my images is using a whopping 12MB of memory compared to the 282kb I thought it should be using. A total of 60MB from swiping through 5 images. Whats even weirder the bitmap being loaded is 1020x1812 - much larger than what I provided.

Android Drawable Folders

So I go to the Android docs and after reading through - one line catches my attention.

When Android does not have suitable images provided for a specified density it will get a copy from whatever is available and then scale up/down the image so it appears correctly.

In my case the images are all in the drawable folder (equivalent to mdpi) and the device I am testing on is a Nexus 6 - which is technically a very high density device xxhdpi.

Table with some data goes here

Using the above table mdpi is scaled up to xxhdpi by a factor of 3. Meaning my 340x604 image is 1020x1812. An image this large uses a whopping 12 MB of memory (check out this link).

Solution

Basically I needed to prevent the scaling up of image assets. The quick solution I opted for was to move this image to the drawable-nodpi - which basically says use the same image not scaled up or down for all device densities. This worked well.

The better solution would be to save different images at the right resolutions in each of the different drawable folders so as to make sure the image is as crisp as it can get for each device.

I hope this helps you and I certainly learned something new about drawable folders I did not know was happening all this while.