A lot of blogs and best practices for Android Development says
"You don't need to supply bitmaps for every possible density, Android will scale your bitmaps (typically when they are loaded) to match the current density."
Reference Link : https://plus.google.com/105051985738280261832/posts/6eWwQvFGLV8
I tried it works well.
But i couldn't understand one thing.
For instance i created a sample app with 5 full screen images of around 1.5 MB each in a view pager.
I created heavy images for 7 inch MDPI devices like Galaxy Tab 2 and put them in "drawable-large-mdpi" folder. It was bit jerky but didn't crash and let me scroll through all the images.
Now i tried to use the app on my Nexus 7 which is almost a large HDPI device. The app crashed with "OOM error" while decoding the bitmap.
*If i move the images from large MDPI to large HDPI, it works well on both the devices with out any crash. *
So i have two questions.
Does this result incline towards, we can only put the graphics assets in the highest density drawable folder and let it scale down automatically in its range ?
What happens internally why it crashed for the first time ?
That would be one way to do it so long as the drawables look fine when scaled. Scaling down in size usually is better than scaling up. That said, there is this line:
Even so, when bitmaps are scaled to a density there isn't a design
for, you may get artifacts such as softening of the edges.
The reason scaling from HDPI to TVDPI works fine is because the two densities are so close. Problems with some images will start showing up between the major densities. For example, jumping from HDPI to LDPI will sure result in many artifacts as the image was simply not designed for low-resolutions.
It's most likely crashing with an OOM in the Nexus 7 because it's taking your large images that the device thinks are MDPI images and scaling them up to TVDPI settings. This would result in an even bigger image.
When you put them in the HDPI folder, you're telling it to scale down to TVDPI so the resulting image is a less memory footprint.
There's also the overhead that comes in to play in the actual scaling.
The same thing will probably happen when you scale up to an XHDPI device.
No, you can put graphic assets into any drawable folder. If you only put resource in one density folder, the system will scale up and down to the target device densities.
You wanted to use a lot of memory with using those large images at the same time. Even if you use compressed image formats, when the system draws it on the screen, it will be in the memory as a bitmap. So if you keep 4-5 1200x800 pixel image in the memory at the same time it will use up that amount of memory in bitmap. That crashed your app because it ran out of the granted memory (which is very low by default android only ensure 16 mb which is usually broadened by manufacturers).
When you put the images in the hdpi, system scaled down the images, thats why there could be (not guaranteed) enough memory for the smaller images.
Solution: don't use that big images as backgrounds. Try nicely scalable images, and check out the 9-patch format: link
Related
Until now, I did not pay too much attention to how to store drawable resources.
I usually generate multiple versions of an icon and store them under the drawable-mdpi, drawable-hdpi, drawable-xhdpi, ... folders.
For other images for which I don't have multiple versions, I inconsistently store them under drawable-nodpi or drawable.
However, I recently encountered an issue related that bring all my attention to that. I stored a 100KB image under the drawable folder. However, my app was regularly crashing, stating it could not allocate 18MB!
After some searches, the reason was that the image was scaled to fit the screen resolution and it resulted in a way heavier image. The fix was to move it under the drawable-nodpi folder which prevents that scaling.
So now, I am trying to better understand where I should locate my images and how this scaling effect works to optimize my app on that part.
I have done plenty of searches, but resources are limited or unclear on that subject and the official documentation kinda really sucks.
I am aware of the official explanation for the drawable or drawable-nodpi, but it does not clarify everything.
Typically:
How does the scaling work? Let's say I have res/drawable-mdpi/image.png. Does that mean the image is scaled if I have a screen different from mdpi, or will it also be scaled on mdpi screens resulting in possibly heavier image size on every device?
Following previous question, if the image is not scaled for mdpi screens but scaled for any others, then does that mean that if I provide a version of this image for every screen density, the scale will never happen? Or at the opposite, if the image is scaled also on mdpi devices, then having different version of the same image for each screen density will still scale the image, but using the version of the image matching the screen density of the device?
How to deal with icons for which I do not have multiple versions? I am afraid that if I put this single version in mdpi, it just scales it and uses so much more memory than necessary. In such case, should I put any icon for which I do not have different versions under nodpi?
On the other hand, if whenever you put an image in mdpi, hdpi, ... it scales it even on devices matching the density, then should I just move everything under nodpi or some high resolution like xxxhdpi where it can only be downscaled?
Thanks
Let's say I have res/drawable-mdpi/image.png. Does that mean the image is scaled if I have a screen different from mdpi
Yes.
or will it also be scaled on mdpi screens resulting in possibly heavier image size on every device?
No.
if I provide a version of this image for every screen density, the scale will never happen?
Yes. Your APK will be larger, due to the 7 copies of the drawable.
How to deal with icons for which I do not have multiple versions?
Option #1: Decide what density that particular version belongs in, and put it there. Android will upsample or downsample the image for devices operating at other densities.
Option #2: Put it in -anydpi or -nodpi, in which case Android will not upsample or downsample it. However, in this case, you need to be controlling the size of the image yourself (e.g., in the ImageView).
Option #3: Replace the icon with one that either you have all relevant densities or one that is an SVG that works as a vector drawable when imported using the Vector Asset Wizard in Android Studio.
The decision-making needs to be based both on memory consumption and what the result looks like. A low-memory solution that looks awful is unlikely to be a good choice.
should I just move everything under nodpi
Probably not.
or some high resolution like xxxhdpi where it can only be downscaled?
Probably not. It is unlikely that a massively downscaled version of your icon will look very good.
When I started learning Android, the course that I took mentioned about screen densities in a very early stage.
And it encouraged me to generate image resources for HDPI, XDPI, XXDPI and XXXDPI.
Whenever I get an image from designer, it is a XXDPI image and I will generate other versions using a plugin of Android Studio.
Later I started to doubt if this is really necessary.
As far as I can observe, the only benefit of having different resources for different screen density, is that I can use wrap_content for ImageView and the image will keep the same size in different screen densities.
But this can be completely replaced by using a dp value for the ImageView.
On the other hand, I observed quite a number of drawbacks:
Since multiple images are included, APK size increase (not 100% sure, may someone confirm?)
This approach can sometimes increase memory usage, if you unnecessarily used a very high resolution image (e.g. a fullscreen background image). See this post for example.
Extra effort of generating different images
It is a nightmare if you need to have different drawable resources for another product flavor
So I start to think that why not just put a high resolution image in nodpi folder and let Android scales down for you? I never observed any observable drop in image quality. Will this cause any memory issue in a device with lower screen density?
When reading questions on SO and "supporting multiple Android screens", it always says to place your images in 4 drawables. But wouldn't it work if you place the image in the highest resolution (say xxxhdpi) and let Android scales it down? . Scaling down will barely be visible ( at least from what I have seen) and saves the app a ton of redundant space
Thank you
You can place all your images to only xxxhdpi drawable folder. It works but on the lower dpi devices, it may cause memory/performance issues. And also bad scaling image quality at some cases.
Assuming you have all images into drawable-xxhdpi folder; if you run your app on a lower dpi device, scaling is inevitable and it it costs much more memory usage.
Scaling is proceed at runtime. But if you resize them before runtime, there is no need to scale them again and again. It makes your app faster and smoother, use less memory.
If you want more performance and no memory issues, resize your drawables and put other drawable folders.
I don't get why it's recommended to have a drawable (image) for all densities (ldpi, ..., xxhdpi).
Would it not be better to add one file with the highest needed density (xxxhdpi) and than programmatically scale that image down (e.g. for tab icons I could just set a explicit dp size)?
Therefore I would only have to manage one file and the APK file size would be smaller. I think the performance disadvantage should not be significant.
Update 1:
To be more specific: I never noticed any quality loss when using a high density image (PNG file) which was scaled down to a explicit dp value on my mdpi device.
So I was considering if the disadvantages of managing multiple image files as a coder without a designer (and the higher APK file size) might outweigh the advantages. Especially if I'm going to target newer devices (API >= 17).
Update 2:
In my case I'm more a coder than a designer. In the microphone example image of #mes I could just use a high density version of the left microphone and scale it down with no significant disadvantages?
There's no significant advantage, when designer or you simply reduce image size and put inside the ldpi, actual meaning is to paint another image for small size, because there are many cases, when simply reducing an image size produces inacurate and low quality image, and a good designer will draw another image, with few details in it, this is an example, only in this case worth to put different images in different folders
Therefore I would only have to manage one file and the APK file size would be smaller. I think the performance disadvantage should not be significant.
Yes, you are right, there no significant performance improvements, and also apk size is increases, that's why not worth to downscale the same image.
There are two reasons for this:
Typically, devices with lower density screens also tend to have less powerful hardware for image & graphics processing, along with lower memory.
Its not just the density, it is also the file size and image dimensions that are scaled accordingly.
The Android way of having different drawables for different screen form factors is thus appropriate from a technical standpoint.
First advantage is what mes mentioned: different images for different sizes.
Second advantage is memory/code efficiency: a 1000x1000 pixels image on android will take 1000x1000x4 ~= 3.8 mb of memory which is really wasted on smaller screens which can have 16mb available memory per app. You could try loading the bitmaps efficiently but that's a lot of code to put/maintain for each image and also will have performance issues on some cases.
I've recently started testing for my game on other peoples phones. For some reason the whole game is getting really "blurry" for their phones, but not for mine. All of them has got a higher android version then I do.
I've tried finding a good answer around the web for this, but all I can find is the answer for if you are using the res/drawable folders. I am loading my images from the assets folder with a special load method. I am then stretching them out when I draw them onto the screen with the "c.drawBitmap()". I am drawing using an "android.view.View".
So now, anyone that has the answer for turning the anti-aliasing off for bitmaps when creating 8-bit games. It would be an impossibility for me to resize the images before rendering them onto the screen, because of me creating an image from an integer array, that refreshes with a rate of about 60 fps.
That blurriness, is most probably because of low resolution of your bitmaps. It may be fine on the test device of yours, but when you use them on higher resolution screens you will either get raw/pixelated or blurry/AA'd results after scaling. An appropriate solution is to have different images for different resolutions, or to be more accurate; densities. Android already do the choosing part:
ldpi
mdpi
hdpi
...
I am assuming you know these identifiers.
Put your high resolution image to res/drawable-hdpi, low resolution to res/drawable-ldpi etc.