I have an activity that set a background image. And it causes OutOfMemory in some devices when I keep enter and leave this activity.
Suppose I am using x-hdpi device. Is that I would use up more memory if I put a image in hdpi bucket than put in x-hdpi bucket because the image need to be scaled up if I put in hdpi bucket? Actually, how to determine which bucket should I put the image to?
And Is there any way to avoid OutOfMemory of this case?
I try to call recycle and setImageResource(null) in OnDestroy. But when I enter that activity again, I would have exception "cannot draw recycled bitmap".
Answer to Second Question.
Before Decoding image and setting to background you should resize image to Avoid Memory Overflow Error. This Error generally comes when we load image inside Memory. See how to Display Images Efficiently
Answer to First Question.
Putting same image to different folder will not cost memory as if you show LDPI image to HDPI device it will stretch but will take the same memory(All though it will look weired). But if your designer make Image differently for each density then image size will differ then it will take some more or less memory
Related
I'm taking whole screenshot of a WebView and display the bitmap on an ImageView. The bitmap can be 7 screen height. (E.g. 1440x14000 px)
I'm frequently face with
OutOfMemoryError.
I've seen this
This says that load a scaled down version into memory but I don't want to lose image quality. There are the same approaches on the web.
Is there any way to handle OutOfMemoryError without loading scaled down version?
The bitmap can be 7 screen height. (E.g. 1440x14000 px)
Note that this means that the user cannot see the whole image at once at full resolution.
I'm frequently face with OutOfMemoryError
On most devices, you will have a very difficult time loading an image that large, as you cannot get a single contiguous memory block that big.
This says that load a scaled down version into memory but I don't want to lose image quality
To some extent, you do not have much of a choice. If you want the user to see the full extent of the picture at once, the image has to be scaled to fit the screen.
Is there any way to handle OutOfMemoryError without loading scaled down version?
There are ImageView replacements that offer pan and zoom. Some of those, such as this one, handle loading in pieces of the image at a time, with whatever scaling is necessary for the current zoom level, to make it more likely that you will be able to show the user the entire image.
It is not a solution, of course, but I'm also not familiar with your exact needs, so maybe this may help you a little - you can try to play with bitmap options during decoding. Try to use Bitmap.Config.inPreferredConfig as RGB_565 - this will reduce size of your bitmap twice comparing to default ARGB_8888. But, of course, if you use complex images in your web page this may reduce their quality.
So, I have been using this amazing library Glide for showing native images in my gallery app. I am using ViewPager with FragmentStatePagerAdapter to show full size images. The pager's off screen limit is 1 (default to save memory). I am using this code to load images into ViewPager in my fragment:
Glide.with(getActivity())
.loadFromMediaStore(uri)
.asBitmap()
.signature(new MediaStoreSignature(mimeType, dateModified,
.into(mImageView);
Now, I am facing some issues here like:
Images take quite some amount of time to load (if not cached). So, while user is scrolling through the viewpager blank screen is shown while image is loading which is what I want to avoid. Is there any way I can do this? Maybe by precaching images?
Sometimes, while scrolling through large size images (mainly Camera photos) OOM Exception is thrown and user is left with blank screen as no image is loaded. This also happens when I am shifting from potrait to landscape mode. So, I tried to use methods like atMost() -- which degrade the quality of image further as images are already loaded in RGB_565 and approximate() which is also causing OOM. How can I achieve maximum image quality without getting OOM exceptions?
For the second issue, I was thinking to load lesser quality images for the off screen items and then enhance quality when they come on-screen. Is it possible?
I have also tried to use ARGB_8888 but the result was same: OOM exception.
TL;DR
Make sure the ImageView has match_parent or fixed dp as dimensions
wrap_content makes Glide load full resolution Bitmaps.
.placeholder() shows an image instead of empty space while loading large bitmap
.thumbnail(float) loads a downsampled version fast while the bigger image is loading in the background
Also look around the Glide issues, maybe you find something helpful.
Details
I would be curious what the xml is for the ImageView, because my guess is it's wrap_content which results in loading images at full resolution into Bitmaps (using a lot of memory). If that's the case I would suggest using match_parent or fixed dp to lower the resolution. Note: you won't use detail, because currently the image is downsampled at render time anyway, just pulling that forward to decoding phase.
You also have to make sure that your app doesn't have constraints for memory usage. Can you load 3 (off screen limit = 1 means 1+current+1 pages) camera photos into Bitmaps without Glide? Again, assuming this is full resolution, it should be possible to store 3 screen size amount of bytes in memory with or without Glide, but you have to guide Glide to not load at full resolution.
You can load smaller sized image via .thumbnail(), it accepts a full Glide.with... not including .into() OR there's a shorthand parameter which is just a percentage (in 0.0 ... 1.0), try the latter first. It should decode the image much faster, especially with a really small number like 0.1 and then when higher quality one finishes it's replaced.
So the simpler option is to add .thumbnail() to your current load. A more convoluted one involves to start loading a lower resolution image with .sizeMultiplier() at the same time the Fragment's view is created and then start loading the high resolution one when the ViewPager has changed page. This helps with peeking the pages.
Alternatively you can just use a .placeholder() while the image is loading so it's not empty space, but "something" there.
Regarding using ARGB_8888 (32 bit per pixel): if you increase the memory consumed by Bitmaps (compared to RGB_565 (16 bit per pixel) don't expect to get run out of memory later. Once you get it working with 565 you can try increasing, but until then it's futile trying.
Also look around the Glide issues, maybe you find something helpful.
Actually I have for so long wish to know how to present graphics in a proper way.
In an activity, I have the following:
a background (png, full screen, 768*1280, 1.36MB)
3 icons (each icon has pressed and not pressed: 2 states, using 1 png 400 * 400, 300KB each), i.e. 3 icon * 2 pic * 300KB = 1.8MB
some more textviews
When the app starts off and directly goes to this activity, everything is ok, the activity can be presented properly.
Yet somehow when the app has run for some other activities, and then goes to this activity through a dialog box, then most of the time errors will occur, as follows:
Out of memory on a 15728656-byte allocation.
Question:
I have researched for sometime and some say to bitmap.recycle(), yet how to implement? through the onCreate? or actually 400*400 is too big?
If I want to change the background of an activity upon users' choice, i.e. when he presses button A, the background changes to bgdA, presses button B will change background to bgdB... in that way how that can be achieved?
Many thanks!
Depending on where your asset is stored is the amount of memory it might take, since scaling factor is calculated between the difference of densities, this is a little gray area because I haven't found any official android documentation that backs this info up, however I've seen that error so many times and this is the way I handle it.
1.- If you don't have the asset in the proper drawable-(density), this will cause problems because depending on the devices you are actually supporting, you should put the asset in drawable-xxhdpi or drawable-xhdpi, you will notice how the memory will decrease considerably
2.- If you don't want to mess with densities because it's a generic image which don't have much details(like a simple background), then add the asset in the drawable-nodpi folder, it will prevent android from trying to scale the asset it self..
3.- As good practice, try to create the asset with the proper size for the proper densities, 400 x 400 seems like too much for an icon, this will also prevent you from OOM, not only in this activity, but for other activities that might also need to load a good amount of assets, giving scalability to your app..
Always take on count that leaving the "resize" of an Image to the OS might cause huge amounts of memory allocated because the OS will try to resize it based on the formula width * height * 4bytes, the 4 byes are for ARGB of each pixel, 1 byte per color or alpha, so if your image is for example 1090 * 1920, it could easily become internally 8.3MBs even tho the actual image size is only a few KBs, and if it tries to scale it, it might double it's size too.
Hope this Helps
Regards!
Make sure you have a copy of your image for every drawable folder in you res, for example if you runnig your app on the S4 phone and you don't have all the images in the drawable-xxhdpi folder you will run out of memory even with reasonably small images.
Also if you need to change background at run time use setBackgroundResource.
Hope it helps
This is the common problem in android here is the proper solution
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
In easy words you have to scale the image down according to your requirement
My application use a little memory, it's about 3.4MB, in some old android devices such as GT-I9001. But when i running it in HTC one, my application use very more memory.
Look, the allocated memory is 26.881MB, it's too big, and the free memory only have 2.940MB. Then i use MAT tool check the memory leak, i find the resource bitmap use mach memory.
I can't explain the reason. My application often out of memory. I think Maybe the problem is caused by high screen resolution. If someone also encountered this problem, please join the discussion, thanks!
I debuged the problem, and found some reason:
The onCreate() function in my start activity, and you can see the breakpoint. The application only use allocated memory 3.4MB before calling the setContentView(R.layout.welcome) to load layout xml. Then the application run to next step, it use allocated memory 19MB. So i think this problem must be caused by loading the layout xml.
I modifed the "welcome.xml" file, deleted all widgets, that only have a "RelativeLayout"
But the program also use 19MB memory. Finally, i deleted the backgroud of RelativeLayout and the program memory return to normal size, it only use 3MB.
The size of pictrue "loading_background.png" is only 21KB, i think that perhaps the high screen resolution of high-end device changed the picture size in memory, i will try to use 9.png picture. If you understand this part of the problem, please join the discussion, thanks!
It's not a memory leak if you use big image for background.
File size doesn't matter. When it is loaded into memory it takes width * height of the image * 4bytes.
Use small 9-patch images or shape drawables when possible.
This problem can be solved using drawable-nodpi, look this:
Android background image memory usage
Just try to make a bitmap from 3MB PNG file. You will get a 20MB picture. That's why it's beter to convert your images from PNG to JPG. The quality is not so much different actually, but you will profit a lot from memory side.
just add high resolution images to the xxhdpi folder in drawable. this prevent android scale up the image to the ultra sizes
I am developing an application for android and it contains several images and I have to get them from drawable, so I am using bitmap for them to avoid out of memory exception by rescaling them, but the problem is when rescaling, the resolution (/quality) of the image is reduced. How can I overcome this problem?
You can keep a reference to the original byte array and check your bitmap size against it but I would just use pngs in the appropriate drawable folders. Rescaling/reloading the image yourself and checking for quality could be much more memory hungry in a garbage collected language.
Let android do it's thing..
The only way you can avoid the loss in quality is by scaling the image proportionately in terms of width and height.
For eg : if your resolution is 100x80 try reducing it to 5x:4x . That will somewhat preserve the quality.