I have a flags.png file that's 24480x160. I put this file in the "drawable" folder and loaded it into a bitmap on an emulator that's 420dpi (xhdpi). I generated a heap dump and checked the hprof. The bitmap drawable was occupying 8.7 MBs. The heap size was 72 MBs with 56 MBs allocated.
Doing the same exercise with the image in the xxxhdpi folder, the bitmap drawable remains the same size, but the heap size becomes 28 MBs with 17 MBs used.
I understand that "drawable" is considered to be mdpi and that Android will do some scaling when loading the image. It seems to be doing the same thing with hdpi and mdpi drawables on an xhdpi device as well. What I don't understand is what exactly happening that's resulting in the same 8.7 MB image but with different heap sizes. More specifically, if it's going to result in the same 8.7MB image, why scale in the first place and cause a large heap increase?
Related
I understand that we import different sizes of an image file into the different drawable folders (hdpi, xhdpi usw) to avoid downscaling on different screen resolutions. This costs memory and could make the image look distorted.
This is what I don't understand and i have found no proper explanation anywhere:
Does the image in pixels have to perfectly fit the containing ImageView's size or is it just important that the image comes close to it? Because in both scenarios the process of downscaling will happen.
Is the process of downscaling the memory intensive part or is it the size of the image file that is in the cache? For example: Is a 10% too large image better for performance than a 200% larger image?
When I prepare image assets, is it enough when they come close to the target size or do I have to make sure that there will be 0 scaling?
Lets say i have an ImageView with 100x100dp and an xhdpi screen draws 2 pixels for 1 dp, does my drawable have to be exactly 200x200px or can it be like 220x220px?
I talk about local images in the drawable folder here.
While adding a ViewPager and running into some OutOfMemory errors, I realized that my default method of ImageView.setImageResource(resId) was causing 9x more RAM to be allocated than need be. In my example, I had a 1920 x 1080 image which at native resolution was slightly bigger than the ImageView object it was placed in. I could expect some downsizing of the image but would not expect more than 12.6 MB to be allocated (1920 x 1080 x 4 bytes). However, 74.6 MB was allocated per image, and with 4 images loaded this quickly blew the VM memory budget away.
To solve this, I changed the method to the slightly longer code below - now each image is allocated exactly the native amount of memory expected (12.6 MB), and the images still load quickly and look great.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inSampleSize = 1;
Bitmap bm = BitmapFactory.decodeResource(getResources(), resId, options);
mImageView.setImageBitmap(bm);
For reference, my device is a Nexus 5 with 1920 x 1080, xxhdpi, 3x scaling factor (dp to pixels). I imagine that internally the OS is scaling the image up 3x3 (=9x) to match the scaling factor, but that does not make sense since the original image is at native resolution of the full screen size.
What is the reason that they are initially allocating so much memory, and is there a proper way of setting the image resource to avoid this memory waste?
Drawables in the drawable folder are treated as mdpi (i.e., 1x). Therefore the system will automatically upscale the image to the native density, growing it 3 times in size in each direction, for a total of 9x the memory usage.
You should move the image to drawable-xxhdpi if you want the system to use it as its native resolution and the system will downscale it for lower density devices.
If instead you want a fixed number of pixels on all densities (i.e., a different physical size on screen for different density devices), you can use the drawable-nodpi folder.
In my android app, the drawable folder has a total size of 1.1 MB (28 compressed pictures)
is that the MAX amount of memory that will be used by my pictures, or is it calculated differently?
thank you :)
is that the MAX amount of memory that will be used by my pictures
No.
is it calculated differently?
Yes. Generally speaking, each image will consume 4 bytes of heap space per pixel, based on the image's resolution. IOW, you are calculating things based on the size of compressed images (PNG, JPEG) on disk. What matters is their size in memory.
The calculations also depend upon whether a given drawable is used as-is, or whether is is down-sampled or up-sampled based on a density conversion. For example, if you have a 512x512 image in res/drawable-mdpi/ named foo.png, and you load R.drawable.foo on an -xhdpi device, and there is no other version of foo.png (e.g., in res/drawable-xhdpi/), then Android will up-sample your 512x512 image so that it is the same on-screen size on the -xhdpi device. That will turn the image into a 1024x1024 image, quadrupling the heap space usage.
I've got a program which is loading resources from the resource file (by setting android:src="#drawable/image" in the layout file.
The image is 800 x 1280 pixels. When the program runs it allocates 16,384,000 bytes on the heap which is 16 BYTES per pixel.
I assumed that it should default to RGB8888 which only uses 4 bytes per pixel. Anyone know how I can change this to use less memory?
Your images could be stored in an inappropiate folder. If you have high resolution images in a low resolution folder, let's say, the images are 800 x 1280 and you're storing it in drawable or drawable-hdpi, Android will scale them up to match xhdpi (although they are already in xhdpi, so to say). The scaled image occupies more memory.
Solution, put them in drawable-xhdpi.
you can import them using BitmapFactory, Options, and inSampleSize to reduce the memory footprint.
Also, that seems like a very high res image to display on a mobile screen so you could also reduce the size?
Check the documentation about bytes per pixel: http://developer.android.com/reference/android/graphics/Bitmap.Config.html
You can try using Color.RGB_565, but the image may lose quality.
This might be a quick answer but I must not know what to search for as I can't find anyone talking about it! I have some png files in my drawables folder that are 88x88 pixels. I run my app and have a log print out the size of the images and it claims they are 176x176. What gives? Is something expanding my images before they go into the apk or is my phone really only half the resolution it claims?
BitmapFactory.decodeResource scales images in the Drawable folder according to the DPI of the device (it generates a higher resolution image for high resolution screens).
If you don't want the image to be scaled on decoding, place it in the folder Drawable-nodpi
From this guide:
"By default, Android scales your bitmap drawables (.png, .jpg, and .gif files) and Nine-Patch drawables (.9.png files) so that they render at the appropriate physical size on each device. For example, if your application provides bitmap drawables only for the baseline, medium screen density (mdpi), then the system scales them up when on a high-density screen, and scales them down when on a low-density screen. This scaling can cause artifacts in the bitmaps. To ensure your bitmaps look their best, you should include alternative versions at different resolutions for different screen densities."
So yes, your screen is probably xhdpi with density 2, so size of your image from unspecified drawables folder would be x2.