I'm having serious issues with memory, and I'm looking forward to recycle bitmaps.
However, what my app is doing atm, is building a Gallery (yeah, the deprecated) with several bitmaps, and then, several Galleries.
So at the end of the day, the app looks like (LinearLayout style):
*Some webviews
*A Gallery with 7 images.
*Some webviews
*A Gallery with 10 images
and so on.
So what I'm thinking is... Once I've displayed those images, and them are on the screen, can those bitmaps be recycled?
Is there any way to recycle a whole Gallery component?
Thank you so much.
Edit:
I've tried soo many things. I'm still getting the error java.lang.IllegalArgumentException: bitmap size exceeds 32bits
This is my actual code:
BitmapFactory.Options bfOptions=new BitmapFactory.Options();
bfOptions.inDither=false; //Disable Dithering mode
bfOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
bfOptions.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
bfOptions.inTempStorage=new byte[32 * 1024];
bfOptions.inInputShareable=true;
bfOptions.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeFile(mUrls.get(position).getPath(), bfOptions);
inflatedImageView.setImageBitmap(Bitmap.createScaledBitmap(bitmap, 120, 120, false));
As you can see, I've set many options to the Bitmap, aswell as resizing it, and lowering it's quality. Still happenning the same issue.
First you should never manually call system.gc, and generally there is no need to manually recycle bitmaps either. Let the operating system do what its best at, deciding when to take the huge hits to run garbage collection.
One of the reasons that gallery was deprecated is I am fairly sure it just loads all items as soon as it renders. And does not load them and recycle them on demand like a list view. Which means it is a huge waste of RAM and no amount of recycling will help you. In addition Gallery is very glitchy on 4.0+ phones, which is the majority of the Android user base at this point. I would strongly recommend you move away from using gallery.
There are two alternatives.
1) View Pager which is now built into the Support V4 library
2) HorizontalListView --> https://github.com/MeetMe/Android-HorizontalListView
Also be absolutely sure you are downsampling all images prior to loading them in the image views. To learn how read http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Ok. I solved the issue I was having; I had a linearLayout with gallery components and webviews. If I didn't render the webviews the app didn't crash.
So after some tweaks now it's working correctly without crashes:
wv_detail.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
wv_detail.setLayerType(View.LAYER_TYPE_NONE, null);
Related
My app takes an image using the device camera, then opens it so the user can perform some processing. To do this I check the image orientation and rotate it if necessary, which I do like this:
Bitmap image = MediaStore.Images.Media.getBitmap(this.getContentResolver(), lastTaken);
int rotation = getCameraPhotoOrientation(filePath);
if (0 < rotation)
{
Matrix mx = new Matrix();
mx.setRotate((float) rotation);
image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), mx, true);
}
This code works on most devices, however on one of the older test devices I have been using, it causes an OutOfMemoryException when the image is rotated, presumably because it is trying to hold two copies of the image in memory.
How can I avoid this?
If there isn't an easy way to prevent the problem at this point in the process, is there a way that I can test how much memory I have available and limit the image resolution the camera uses to one that will leave enough room for this operation? I'm already specifying resolution, so it is really a question of how I can compare the image size and available memory to figure out what resolution would be safe. My one constraint is that the changes the user makes have to be on the original image, so editing a smaller copy of the image won't cut it, unless there is an easy way to reflect those changes on the stored version. Consequently downsizing the image to something that can be supported in memory wouldn't be a disaster by any means.
What is the size of your image? Did you check for possible memory leaks in the App?
Anyways, one way is, you can try to reuse the memory allocated to the first Bitmap after a new second Bitmap content is created.
As per Android docs here - https://developer.android.com/topic/performance/graphics/manage-memory ,
Android 3.0 (API level 11) introduces the
BitmapFactory.Options.inBitmap field. If this option is set, decode
methods that take the Options object will attempt to reuse an existing
bitmap when loading content. This means that the bitmap's memory is
reused, resulting in improved performance, and removing both memory
allocation and de-allocation.
You can set your Bitmap image to be recycled when a new Bitmap is created.
I couldn't find a way to figure out the amount of memory available and how much I would need, but after finding some other questions on this topic I found that the best solution for me was to enable the largeHeap element in the application manifest - that resolved the crash for me, although it didn't answer the question and after further reading I suspect there is no satisfactory way to do this as things stand.
I have a long ListView in which there is an ImageView for each row. It displays the correct bitmaps if I slowly scroll the list (each row has different icon to show).
The problem comes up when I scroll the ListView fastly. It happens that many images are not loaded into their ImageView, leaving it transparent. Even the ones that were previously shown scrolling the list slowly.
Here is the code inside the getView() method that should display the icons:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
String name = ... //custom code to get the icon name to show
Bitmap bitmap = BitmapFactory.decodeFile(getIconsDir() + name + getIconsExt(), options);
holder.imgIcon.setImageBitmap(bitmap);
I'm recycling the convertView in the getView() method of the adapter.
I know for sure that the bitmap exists in that location.
imgIcon is the ImageView referenced by the ViewHolder of the row.
I've also tried to use image loaders (like Picasso) but I got the same result.
Does anyone have experienced this before?
Do not do bitmap decoding in adapter's getView(). It will Cause ListView to lag and also fill up RAM very fast.
You should:
Use a size limited, in-memory cache to hold onto bitmaps.
Decode and Load images in Views asynchronously.
ListView re-cycles the views, and device memory may not hold all the bitmaps loaded all the time. Even Image loading Libraries face that issue.
Yes, there are performance issues with images in ListViews.
But prefetching the images in any way is a could approach to avoid the calls to read operations on the file system and decoding of binary data.
I extinguish that you are storing your viewholder in the convertviews - tag.
Loading images from disk is a slow operation, and so is decoding them from whatever compressed format they're stored in (like JPG or PNG) into a Bitmap. So ideally, it shouldn't be done in getView() on the main thread. Also, they take up a lot of memory.
If these "icons" are static, you should just include them as drawable resources and then use ImageView.setImageResource. Then the OS will figure out how to load them most optimally (plus, that way you'll have the correct resolution for the given screen).
If the icons are not static (e.g. loaded from the network), I recommend using an in-memory cache with an asynchronous loader, but make sure it's limited in size and/or uses weak references (but beware: Android generally advises against using weak/soft references, especially for bitmaps, since the memory allocated for bitmaps is in native code, so the garbage collector doesn't know how much memory those images are really taking up and it may decide not to collect them even if it's running low on memory... because it thinks they're small).
I have a gallery working in my app at the moment with about 8 pictures on it. Before i had a bug that use to make it crash because of overload Heap (something like that, not sure).
This was caused by my pictures being too big in file size, so i reduced them it worked. So my main question is How many picture can i actually put in to my app.
I was hoping to have about 5 screens (activities) with some scrolling pictures
I don't want to start changing my app if its just going to crash again
Also does anyone know how to add transitions when scrolling through pictures, for it too look fancy
Everything depends on the way you manage you pictures. To save the memory (and increase the number of images loaded) you should load a resized picture.
You can subsample picture while loading:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2;
BitmapFactory.decodeResource(res, id, opts);
This will load a picture scaled 1/2 of it's original size (keep in mind that subsampling works only for power of 2 values). You can load the picture smaller than the area it will be displayed in and let ImageView to upscale it.
If your loaded image is still too large, you can scale it more with Bitmap.createScaledBitmap().
You should be aware that pre-Honeycomb Android doesn't recycle bitmaps like post-Honeycomb Android does. You should call Bitmap.recycle() as soon as the bitmap is not used anymore to clear the memory.
For image scrolling you can use ViewPager.
Further reading: http://developer.android.com/training/displaying-bitmaps/index.html
There is no strict limit on the number of pictures you can include in your app. See the stock Gallery app as an example, I consider myself a relatively light picture taker, but I still have hundreds of images in my Gallery. If you follow the Bitmap best practices you shouldn't run into any memory issues, and you should be able to include as many images as you want.
My tablet app has to display a very large image (2500x6000) and allow the user to pan across, zoom in & out. Since it can't fit into memory I've broken it into tiles and am loading those as needed. The problem I'm running into is that whenever I unload or replace a bitmap I cause garbage collection which pauses my app with noticeable stutter. I was wondering if anyone had come up with any clever ways to work around this? I create my bitmaps using BitmapFactory.decodeResource. I've tried replacing the same bitmap but garbage collection still runs (assuming it dumps the old bitmap and replaces it with a new one).
Thanks!
Figured out the answer! In API11+ BitmapFactory.Options has an attribute called inBitmap which will reuse the bitmap when loading content. I've implemented it as such:
mBg[i] = Bitmap.createBitmap(800, 1232, Bitmap.Config.RGB_565);
mBgOptions[i] = new BitmapFactory.Options();
mBgOptions[i].inBitmap = mBg[i];
mBgOptions[i].inPreferredConfig = Bitmap.Config.RGB_565;
mBgOptions[i].inMutable = true;
mBgOptions[i].inSampleSize = 1;
The garbage collector no longer runs and the pauses have been removed.
As an f.y.i, inSampleSize has to be set or it won't work.
If you are targeting Android 3.0 then this answer may help:
How to load tiles from a large bitmap in Android?
Pre-Android 3.0 you could build an HTML page of your tiled images and let the built-in browser handle the load and unload of the images. My answer here has some more details:
How to tile and scroll a large image (10000x10000) in android
Anyone else have any alternative approaches?
Are you using android:largeHeap="true"? That might reduce the frequency of GCs. Also given you are targeting tablets, then you can safely assume that you are dealing with a concurrent garbage collector. So it will work best if it has more, smaller chunks of memory to collect, i.e. smaller tiles.
I used this easy to integerate source of WorldMap application:
https://github.com/johnnylambada/WorldMap
This uses a huge image of a world map, and uses cache to display a map.
To integerate, I just copied all the java files (5 i guess) and used the surfaceView in my layout file. Then I went through the small OnCreate() method of ImageViewerActivity.java and used the code in my activity (with sligh alteration, depending on my personal use).
When displaying a gallery with lots of large bitmaps (fullscreen, wvga), I quite frequently get out of memory issues. I assume this is related to bitmaps not being recycled. When/how can I force bitmaps to be recycled?
I also noticed that in the getView method of simpleCursorAdapter, convertView is always null. I assume this means that the old view is not recycled? Even when scrolling back and forth, a new view is created each time. However, scrolling back and forth does not cause out of memory issues, that only happens when the total number of images is large enough.
When using the same adapter with a ListView, views are recycled, so it seems the problem is with the Gallery.
What can I do to force views and/or bitmaps to be recycled? What else can I do manage memory without reducing gallery size and bitmap quality.
You can use recycle() if you are sure tou won't use a bitmap anymore, for example in any operation with a temporary bitmap. But...You can use BitmapFactory.Options, for ex:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inPurgeable=true;
Bitmap bitmapTemp = BitmapFactory.decodeResource(getResources(), R.drawable.intro, bitmapOptions);
What it does? the option inPurgeable will asume that once the system needs memory and your bitmap it's no more useful, system will recycle this memory allocation by itself. If for some reason you cannot load an image with the BitmapFactory then you can use Recycle() and it's recommendable to call de garbage collector then with system.gc(), this is usefull if you're developing for example a game, because when garbage collector it's activated when you system it's low on memory, it will consume some valious milliseconds.
You should be very careful with bitmaps, they're not stored on the "normal" memory used by your app, it's stored on a "general" memory used by all your apps, unless you are using Android 3.0.
If you get a memory leak and can't find it looking at the heap... you should take a look at your code loading and using bitmaps.
Hope it helps.
You never need to recyle a Bitmap. However, you can do it, and it will help when you have OutOfMemory errors. But don't it until the bitmap is not needed any more, because (from the javadoc)
it will throw an exception if
getPixels() or setPixels() is
called, and will draw nothing. This
operation cannot be reversed, so it
should only be called if you are sure
there are no further uses for the
bitmap.
In your case, I assume you should:
use thumbs which are bitmap opened with BitmapFactory.Options.inSampleSize or MediaStore.Images.Thumbnails
Have only at most one full size bitmap in memory (maybe one preloaded in you can afford)