Reputation: 7846
Originally everything was fine. I had been working with the 5 drawable directories
drawable-hdpi
drawable-ldpi
drawable-mdpi
drawable-xhdpi
drawable-xxhdpi
that were created for me by the eclipse adt-bundle when I created a new project recently. So when I needed a new bitmap, I had been putting a copy of the bitmap into each of these 5 directories. This always seemed a bit strange to me, so I was happy to discover by reading the Android documentation Providing Resources that I could have a single directory 'drawable' for all the common bitmaps. So I switched to
drawable
drawable-hdpi
drawable-ldpi
drawable-mdpi
drawable-xhdpi
drawable-xxhdpi
The only file that was left in the original 5 directories was the 'ic_launcher.png' file had been created for me when the project was originally created.
However, the new configuration of drawable directories failed badly. Loading up a 36k bitmap file frame.png, an out-of-memory error now occurs as a result of the line
bitmap_fancyframe = BitmapFactory.decodeResource(getResources(), R.drawable.frame);
The logcat shows that Android now tries to allocate a 6790156 byte memory block as a result that line (which is the line DrawOnTop.java:95 mentioned at the bottom of the logcat)
06-01 07:26:53.995: I/dalvikvm-heap(1530): Forcing collection of SoftReferences for 6790156-byte allocation
06-01 07:26:54.025: D/dalvikvm(1530): GC_BEFORE_OOM freed 9K, 14% free 53820K/62343K, paused 27ms, total 27ms
06-01 07:26:54.025: E/dalvikvm-heap(1530): Out of memory on a 6790156-byte allocation.
06-01 07:26:54.025: I/dalvikvm(1530): "main" prio=5 tid=1 RUNNABLE
06-01 07:26:54.025: I/dalvikvm(1530): | group="main" sCount=0 dsCount=0 obj=0x41077508 self=0x40de49a0
06-01 07:26:54.025: I/dalvikvm(1530): | sysTid=1530 nice=0 sched=0/0 cgrp=apps handle=1075179312
06-01 07:26:54.025: I/dalvikvm(1530): | schedstat=( 681383064 1229707806 4777 ) utm=53 stm=14 core=0
06-01 07:26:54.025: I/dalvikvm(1530): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
06-01 07:26:54.025: I/dalvikvm(1530): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:623)
06-01 07:26:54.025: I/dalvikvm(1530): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:476)
06-01 07:26:54.025: I/dalvikvm(1530): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:499)
06-01 07:26:54.025: I/dalvikvm(1530): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:529)
06-01 07:26:54.025: I/dalvikvm(1530): at com.example.MyApp.DrawOnTop.<init>(DrawOnTop.java:95)
Can anyone explain why this happens? More importantly, that frame.png file gets scaled and stretched in my code so I really don't need 5 copies of it, so is there any way that I can have all common png bitmaps in the one 'drawable' directory without these kind of problems?
Update Thanks to a comment from @sam and an answer from @ken-wolf, I now understand that Android does a lot of bitmap scaling if a bitmap of the right type isn't available. So when I simply copy the same bitmaps between all directories, my new understanding is that the effect of this is that the bitmap will be loaded without scaling. Something is clearly going wrong in that scaling process for me, but since I'm doing my own scaling, I don't need Android to do it too. So I now think the important question is: How can I setup my app so that just one version of each bitmap is provided, and instruct Android not to do any bitmap scaling.
Upvotes: 0
Views: 2256
Reputation: 655
You add your bitmaps to the drawable-nodpi folders. If you don't want any scaling. Or disable auto scaling from manifest.
http://developer.android.com/guide/practices/screens_support.html#DensityConsiderations
Upvotes: 1
Reputation: 3051
Going straight to the heart of the question here:
How can I setup my app so that just one version of each bitmap is provided...
Just put your bitmap in just one of your drawable
directories. I believe it has to be one with a smaller DPI than the target device. But then, Android will create a scaled version in memory for a device with a larger DPI. I also believe that if you just use the drawable
directory, Android assumes it is medium DPI and you get the same results as putting it in drawable-mdpi
. Note that a scaled image is genuinely a different size (in pixel count), but is approximately the same size in inches/DP. On an xxhdpi
or xxxhdpi
screen, the in-memory version can end up being pretty huge. Example, I had a 640x960 background image from an iPhone Retina version of the product. (That probably belongs in hdpi
and I should generate smaller versions). If you put this in drawable-mdpi
Android scales it, by a factor of 3 on an xxhdpi
device, so it ends up as 1920x2880, at 32 bits per pixel, which is about 22 meg I think.
and instruct Android not to do any bitmap scaling.
To do no bitmap scaling at load time, use the drawable-nodpi
directory. You will have to manually scale at draw time or in your layout (using android:scaleType="fitXY"
or similar, and if you have any code that queries image size, it will always return the same pixel count. Quality is imperceptibly different (if this is the only size you have). Performance is significantly better, and there's no huge image in memory, just the original sized one.
Upvotes: 3
Reputation: 23269
It is good practice to provide different sized versions (actual width x height pixel sizes) of each of your images in each of the corresponding folders. This ensures that Android picks the appropriate one based on the device to deliver the sharpest image. You can put them all in the /drawable/
folder only but this will result in blurry looking images - this is especially apparent at higher densities. Give this a read again, it explains it well: Supporting Multiple Screens
The "default" resources are those that are not tagged with a configuration qualifier. For example, the resources in drawable/ are the default drawable resources. The system assumes that default resources are designed for the baseline screen size and density, which is a normal screen size and a medium density. As such, the system scales default density resources up for high-density screens and down for low-density screens, as appropriate.
As to the bitmap out-of-memory error, this is a complex issue to do with memory-allocation on Android. Unfortunately you cannot accurately tell in advance how much memory you have available, and putting Bitmaps into memory is quite expensive. Without knowing about the rest of your application it's hard to give an exact solution but there are defensive measures you can take to ensure this doesn't happen, including:
Bitmap.recycle()
when you are done with itUpvotes: 1