I'm the sole developer on an app for the company I work for. I'm new at app development and I've nearly finished the app but when I was testing, I noticed the heap size grows while navigating the app up to 3x the starting amount until GC runs and knocks it back down. I ran MAT and saw that it had to be something with bitmaps. I am currently changing the icons to an "on" state every time they are touched. This means I just changed the icons to a different colored image to give the visual appearance of being "tapped" such as below.
ImageView callButton = (ImageView) findViewById(R.id.callButton);
callButton.setImageResource(R.drawable.call_on);
They are then turned "off" from onResume such as below.
ImageView callButton = (ImageView) findViewById(R.id.callButton);
callButton.setImageResource(R.drawable.call_off);
I do this for every icon throughout the app. My question is whether this is causing the memory leak from creating so many ImageViews. Should I set them to null onDestory?
No, you do not need to set bitmaps to null in onDestroy. Especially not if you're using images from resources, which references are kept to in Resources anyway. What you may want to consider is using a StateDrawable and changing the state rather than switching drawable's all the time, just for your own sanity. But this kind of usage shouldn't be causing OOMs unless you have a lot of images or other issues.
Release your bitmaps as soon as possible using setImageDrawable(null) like -
callButton.setImageDrawable(null);
Related
I am using itemTouchHelper to remove a cardView from a RecyclerView on swipe
my cardview is simple it just has a text and an image
but when i swipe to remove the card disappears but is never taken out of memory (i am saying this because the memory of the image never goes away and it causes an out of memory exception)
my code is very simple so i was wondering if there was a step i was messing that tells the recycler view to remove from memory?
my code is exactly like this one
Usually, images are loaded in android memory, scaled according to device screen resolution requirements, and kept there till the process dies or, is killed or, is collected by garbage collector.
It is possible that your image size in card view (or other images in your res folder) is/are large, and images after scaling by android is occupying too much of memory, to cause memory exception. Try reducing size of your image (Try compare size with the icon sizes that google icons has, they all are less than a Kb, amazing !).
If the above does not work, then create a drawable-nodpi folder in your res folder and put large size images in this folder, this will indicate android to not to scale these images and render it as it is. (make sure you test in all screen devices after following this approach, because now the image won't scale in different screen sizes).
Finally, if none of the above helps try running : System.gc() after there is a card swipe action performed by user. This will run garbage collector and your image will be removed from memory. (This approach does not guarantee garbage collector working in all devices, so make sure that you try for above two approaches, they should work).
Hope this helps !
i have an appwidget which starts an activity as user clicks it.
In my Activity i have a gridview containing relatively small Drawables(Images, because drawable could be mor than just Images) but user can size them.
I noticed that it takes too Long to size them at runtime when Scrolling trough the gridview.
I want to prepare an lruCache only one time in my appWidget's onUpdate which is called at the very beginning when the user places the appwidget on the Screen.
The problem
When i define an lruCache in my appWidget with
private final int lruCacheSize = (int) (Runtime.getRuntime().maxMemory()/1024);
private LruCache<String, BitmapDrawable> myLruCache;
...
myLruCache = new LruCache<String, BitmapDrawable>(lruCacheSize) {
#Override
protected int sizeOf(String key, BitmapDrawable value) {
// TODO Auto-generated method stub
return super.sizeOf(key, value);
}
};
Will it only exist as Long as the process exist of the appWidget? Or does the Cache-File from lruCache stays in my cacheDir of my app? Or will it be deleted after the process of the appWidget is finished? If it does exist over the process-lifetime of my appWidget, how can i Access it from my Activity?
I don't want to create everytime the user clicks on the appWidget a LruCache and fill it up with all the relatively small Images the gridview will Need later. I want to do it once, or if user clears the Cache which will be checked every hour(to save battery).
The Question
How can i achieve that? Or is there a much better/ simpler way.
Time is not really (if it happens once at the very beginning) the Problem, i notify the user that the Cache is being prepared when placing the appWidget on the Screen.
Any help is appreciated.
Update regarding CommonsWare answere
Use Traceview to determine specifically why your implementation is slow.
The app Scrolling is slow because i provide three sizes (small, medium, large) and i scale them at while Scrolling trough my gridview.(Because it would take several seconds to scale them once at activity Startup, so i don't want that).
See here what i do in my imageview which will later Show the drawable(which is an appIcon):
android:scaleType="fitXY"
android:adjustViewBounds="true"
This causes the lag because i do this for everyimage depending if selected scale size is small medium or large.
There is no "Cache-File" in your code.
So then i i'm misunderstanding something. Doesn't the lruCache create an Cache-File in the Cache-Directory of my application? If not how does in works?
First, you cannot force the user to install the app widget. Work on solving the actual performance problem, rather than trying to build some optimization that will not help all users.
My "app" is only an appwidget. There is not appIcon like at FaceBook. It is only an appWidget when he doenloads my app. Which starts an activity when you click on the button.
Second, your process can readily be terminated milliseconds after onUpdate() completes. Do not fill a cache, only to have it never be used.
I want to use the Cache which i want to fill with the Drawables in the onUpdate of the appWidget, and then i want to use These Drawables from the Cache in my activity. So i don't understand why i never would use the Cache? Maybe i'm misunderstanding something.
Picasso would give you better performance from the user's standpoint.
Does it fit my Needs after the update right now?
---------------------------------------------------------------------------------------------
Update 2
Since the scaling should be done by the GPU and take microseconds, I have difficulty believing that is your problem. What specifically did Traceview show you that made you think that scaling has something to do with this?
I noticed that the Scrolling is very fluid at medium size because that's nearly the origin size of the appIcon from the PackageManager. If scroll trough large, where only 2 or 3 appIcons are displayed per row (at medium there are 5 or 6 displayed but it is much more fluid)m it lags. (With the same logic behind it). So the only Logical answere can be scaling in the XML at the ImageView. As i commented that XML-Scaling out and scaled the appIcons to the size Large and put them directly scalled to to my Adapter for GridView it runs really smooth( Only one or to really small lags at the beginning because convertView is null??)
onUpdate() will be called much more frequently than the user will actually use your app widget.
That's right after every onclick or at the specified time. But i check if the Cache has been cleared or not, if not don't Change anything, if so load Drawables to Cache.
I noticed that it takes too Long to size them at runtime when Scrolling trough the gridview.
Use Traceview to determine specifically why your implementation is slow.
Will it only exist as Long as the process exist of the appWidget?
It will only exist for the lifetime of the process of your app.
Or does the Cache-File from lruCache stays in my cacheDir of my app?
There is no "Cache-File" in your code.
I don't want to create everytime the user clicks on the appWidget a LruCache and fill it up with all the relatively small Images the gridview will Need later. I want to do it once, or if user clears the Cache which will be checked every hour(to save battery).
First, you cannot force the user to install the app widget. Work on solving the actual performance problem, rather than trying to build some optimization that will not help all users.
Second, your process can readily be terminated milliseconds after onUpdate() completes. Do not fill a cache, only to have it never be used.
Or is there a much better/ simpler way.
Use Traceview to determine exactly where your problem lies. Then, solve that problem. For example, the problem could be that you are loading these images on the main application thread, and using a library like Picasso would give you better performance from the user's standpoint.
The app Scrolling is slow because i provide three sizes (small, medium, large) and i scale them at while Scrolling trough my gridview
Since the scaling should be done by the GPU and take microseconds, I have difficulty believing that is your problem. What specifically did Traceview show you that made you think that scaling has something to do with this?
Doesn't the lruCache create an Cache-File in the Cache-Directory of my application?
No.
If not how does in works?
It is an in-memory cache.
So i don't understand why i never would use the Cache?
onUpdate() will be called much more frequently than the user will actually use your app widget.
Hi guys i am new to android and i posted a question a week ago in this link which basically stated that i was getting a java.lang.outofmemory error when i was using a lot of different backgrounds for my activities.
why am I getting errors when I use different backgrounds in xml
So as a new developer I have searched and searched for a solution as to how to clear the memory as i go from activity but none have been clear or precise. Then i stumbled across this site http://androidactivity.wordpress.com/2011/09/24/solution-for-outofmemoryerror-bitmap-size-exceeds-vm-budget/
which described exactly what i was going through except they use 10 activities and i am only using 4. However when i implemented his code it my project i ended up with null pointer exceptions and after fiddling with his code I ended up back were i started with the same out of memory error.
So can anybody direct me to someone who can show me how to have as many backgrounds as i want with out running out of memory. Or does android as great as it is does not let you simply use more than a certain amount of backgrounds? help?
It's not that there is a limit on the amount of backgrounds, but each background image you load is a loaded into memory as a bitmap and held there until the activity is destroyed. If you are opening multiple activities one after another, each background image will need to be held in memory and so eventually you will get an out of memory exception.
If you set a large background image, you will also experience some blocking on the ui thread, while the image is loaded into memory.
One way around this that worked for me was to use an imageloader. This decodes the image off the ui thread, caches it on disk, loads it into memory and if memory is running low, will clear an image from memory and fallback to the disk cache. You may get a slight delay/fade in as the image is loaded but this is not so bad visually, and when loaded once, will load straight away if you go back to that activity.
Check out Picaso Picasso which is really easy to implement and a great api or Universal Image Loader.
My layouts were all RelativeLayouts and the first child (will be behind all other views) was an ImageView with scaleType centercrop and width and height set to match_parent. When each activity loads (onCreate), just grab a reference to the imageview in your layout and set the required background image using your ImageLoader of choice.
The other option is to have multiple copies of your background image in your resources, with each one resized to perfectly fit your resolutions of choice (drawable-mdpi/-hdpi/-xhdpi etc). This way, you ensure you are never loading images that are way bigger than you need to be displayed and your app will be more forgiving in terms of memory consumption.
So I have an APP that loops and continuously draws many png files to a canvas. In the constructor for the thread, for some of the pngs I declare Drawable and some Bitmap handles and assign them (respectively) like so:
Drawablename = context.getResources().getDrawable(R.drawable.pngresource);
mBackgroundImage = BitmapFactory.decodeResource(res, R.drawable.bckgrnd);
Keep in mind I do both methods, MANY times. (even though most of the images are fairly small)
Well... the problem i'm getting is that when trying to start this app on older devices (specifically like the original droid and older) it force closes with the VM budget error.
From research, I've noticed that this seems to be a common problem. (The app runs fine on all newer devices such as droid x, every tablet at best buy, charge, atrix, etc.)
So my question is could I be doing something better?
Is one of these methods of referencing the pngs superior?
Also What exactly is happening here? I need to be able to reference the Image to draw at any time. In other words, at any given instant I could call Draw on one of the handles.
The fixes I've seen for similar problems involve calling the Garbage collector, but would that help me since I would need the Images later anyway? Or is this exclusively a problem with the way i'm referencing the pngs from the drawable folder?
Sorry if this is confusing, i'm a beginner.
If I called System.gc() after every reference would that help, even though the reference is still stored as a Drawable object?
The common fix:
Resize image size, for example: createScaledBitmap()...
Reduce image quality, Config.inSampleSize setting...
Remove all references to Bitmap objects when un-used (setting references to NULL, of course). If you don't do this, System.gc() will does nothing, remember this! and this method call does not guarantee that the memory will be freed right away.
I wanted to ask this question that was asked a few times before. Sorry if I am re-itterating but it is not clear to me as to what is the best solution here.
The question is "how to clean ImageView bitmap resource after its been used so we don't have references to it in memory?".
Here is an example:
Screen 1 redirects to Screen 2
Screen 2 contains control A (preview of large photo)
Control A contains ImageView B
ImageView B is set when control A is initiated
Everything works fine the first time around. Once control A is done I redirect from Screen 2 to Screen 1. At this point all references of control A or ImageView B or Bitmap that its using should be dead. THEY ARE NOT!!!
I've tried all kinds of solutions including bitmap.recycle(), adding finalize() into Control A, System.gs() and nulling control in variouse places such as onStop() and onDestroy(), and everything else that's on screen 2, nothing works!
The problem is when I revisit the screen second time around so going from screen 1 to screen 2 (i.e. creating preview of photo again) I get out of memory exception. It is my understanding that the reference of the previous bitmap is not cleaned up.
How do I KILL it just before I redirect back to Screen 1?
One thing I noticed. If I reduce the size of the photo by, say cropping or making a smaller size of the image everything goes smooth, few times... before I get same issue. So basically it just takes a bit longer to fill up.
I would really appreciate some solution here as this is critical.
You probably have a memory leak , this video might help you in finding the problem Google I/O 2011: Memory management for Android Apps.
Note : pre 3.1 bitmaps are store in VM heap memory but in native memory , which causes lot of problems in noticing leaks,for further info refer the video
Have a look at how WeakReference is used.