two days ago i noticed something. I have a spinner over a map activity. In the OnCreate() method of the activity i populate the spinner with data. After that i start the heap analyzer in DDMS i begin to open/close the spinner. I noticed the VM allocate memory when i open the spinner items, but when i close it, the VM do no free this memory. I've tried to start the GC, but the memory is still allocated. i did this 20 times one by one and the allocated memory increased from 3.5MB to 7MB. What is wrong? I found an issue in google groups, but they haven't answered yet.
Spinner memory leak
I rewrite all my code in the spinner adapter, but the issue still remains.
I read some advices in this topic
Avoid memory leaks
There is something i did not get:
When a Drawable is attached to a view, the view is set as a callback on the drawable. In the code snippet above, this means the drawable has a reference to the TextView which itself has a reference to the activity (the Context) which in turns has references to pretty much anything (depending on your code.)
What does it mean? If i have a textview and set it a drawable object (i noticed the drawable is static), the textview object has a reference to the drawable object and the drawable object has a reference to the view too? If this is true, they become undestroyable by the GC because they both have references to each other? What is this back-reference (callbacks) dependencе between the objects?
Sorry I can't help you on your Spinner problem but I can have a try on the second part:
Romain Guy post on android developer blog explain two important things.
First:
When you create a View (TextView, ImageView...) you must not create it with the activity Context
// DO NOT DO THIS
TextView label = new TextView(this);
Otherwise the View get a reference to your activity and will never be deallocated.
Instead, when you create a View programatically, you have to use the application context:
TextView label = new TextView(getApplicationContext());
Second:
When you link a Drawable to an View, it keeps a callback on your activity via the Context. If you leave it, it will leak memory when your activity is destroy.
The thing to do to avoid that is to "set stored drawables' callbacks to null when the activity is destroyed" so for example whith an ImageView:
protected void onDestroy() {
imageView.getDrawable().setCallback(null);
super.onDestroy();
}
You have to do the same for the background drawable...
Hope it helps.
Related
In this offical blog site, I read below example of memory leak.
http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html
private static Drawable sBackground;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
To quote the original post "This code is very fast and also very wrong; it leaks the first activity created upon the first screen orientation change. When a Drawable is attached to a view, the view is set as a callback on the drawable. In the code snippet above, this means the drawable has a reference to the TextView which itself has a reference to the activity (the Context) which in turns has references to pretty much anything (depending on your code.)"
I do not understand this part. When the activity is recreated, the onCreate() method will be executed, the static Drawable object sBackground will be attached to the new TextView in the second activity. Meaning that the object sBackground will reference to the new textview instead of the old textview in the first activity, leaving the first activity un-referenced.
Can anybody tell me where is wrong in my reasoning? Thanks in advance~~
Oops, it seems this thread is a duplicate, someone asked exactly the same thing here
Understanding memory leaks in Android application
Sorry for my carelessness.
As far as I know after checking the source of View class, when a Drawable is attached to a view, the view is set as a callback on the drawable. Beside, the view also keep a reference to this Drawable. Please see this link http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/view/View.java#16292.
And because static variable has long lifetime, then every time you recreate activity & set a Drawable to its view, the activity and its Context also keep a reference to the static Drawable. Then take memories as long as variable does.
a very old link about memory leaks talks about a drawable that has a reference to a view (link here) .
i have some simple questions regarding it:
why does a drawable have a reference to a view ?
what does the drawable do to the view ?
does it have a reference to all of the views that use it?
do all kinds of drawable have references to views ?
I was reading Romain Guy's article too. The website/ blog is now gone, Wayback link.
Drawable's have a private field (mCallback) which refers to an instance of a class which implements the Drawable.Callback interface, documented here. View implements this interface, and this callback reference is automatically set by the system when view.setBackground is called.
public class View extends Object implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {}
Why set this field? It's usage is an implementation detail of Drawable, so it's difficult to know what exactly its usage is. "The drawable uses this interface to schedule/ execute animation changes." is all I could get from the docs. I guess the main reason is to call void scheduleDrawable(Drawable, Runnable, TimeToExecuteMeasuredSinceAppLaunch) doc.
Implement this interface if you want to create an animated drawable that extends Drawable. Upon retrieving a drawable, use Drawable#setCallback(android.graphics.drawable.Drawable.Callback) to supply your implementation of the interface to the drawable; it uses this interface to schedule and execute animation changes.
So to answer your questions specifically
why does a drawable have a reference to a view ? To call the interface (scheduleDrawable and other interface methods) to animate itself. And also, "to schedule and execute animation changes."
what does the drawable do to the view ? The drawable calls those methods in the interface.
does it have a reference to all of the views that use it? The Drawable has 1 callback (so only 1 view can be used), and it can set it with setCallback documented here.
do all kinds of drawable have references to views ? If you set the setCallback, yes. It doesn't have to be "animated" to have the reference, because this (setCallback) is automatically done with View.setBackground(Drawable) and ImageView. according to the Drawable docs.
Finally, I find his post confusing, as he glossed over this detail which was the fundamental cause of the memory leak (the Drawable.Callback interface and more importantly, the mCallback field). In the end, the callback in Drawable is stored as private WeakReference<Callback> mCallback = null;. It is a weak reference which should not cause the memory leak he says. Maybe this was a change to Android after his blog post.
EDIT: Aha! It was Romain who then changed it in 2010:
1.why does a drawable have a reference to a view ?
A drawable have a reference to a View due to allow it the intercept the view state , suppose that you have a selector-drawable that when view mode change in example pressed, focus, disable change it background
I have a Service that sends an Intent to my Activity every 0.1 seconds. I use it to update a custom implementation of a Chronometer. Here everything goes right. The problem comes when I want to update 14 TextView I have in a TableView inside a Fragment in my Activity. Here the app is very slow.
The method in my Activity where it receives the Intent from the Service:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long milis = intent.getLongExtra("milis",0);
if( mFragment != null)
mFragment.Update(milis);
}
};
The code inside the Fragment where I update the TextViews:
public void actualizarTiempoJuego(long milis){
// Se recuperan los tiempos acumulados y se aumenta la cantidad pasada como parámetro
for(int i=0;i<7;++i) {
long mCurrentMilis1 = mVectorMilis1.get(i);
long mCurrentMilis2 = mVectorMilis2.get(i);
TextView1 t1 = mListaTitularLayoutLocal.get(i);
TextView1 t2 = mListaTitularLayoutVisitante.get(i);
t1.setText(String.value(milis + mCurrentMilis1));
t2.setText(String.value(milis + mCurrentMilis2));
}
}
Am I doing anything wrong, or is it just that I'm trying to do something very complex in terms of efficiency?
#Sherif brings up a good point about hidden alpha values that bog down your application a lot.
Depending on your platform you may also want to check
<application android:hardwareAccelerated="true"... />
Another thing you can look into that may help performance is not firing off all those Intents. Once you start firing intents you are getting the system involved and depending on how they are getting resolved it may take some extra time.
For this issue I like to use Handlers. They are more light weight than intent.
You may also want to look at AsyncTask. This is basically like a thread, but also gives hooks that run on the UI Thread so you can perform both perform a background operation and update the UI without have to post runnables.
EDIT: Lastly, you can always run your layouts through the layoutopt tool. I was personally told by Romain Guy himself that if your drawing too slow, than you need to draw less. Just check out a screenshot (from a less than ideal view tree, but well within the max) from the profiling tool. You can see how much of the resources view drawing takes up. It's very important to keep this as lean as possible if you want your app to be responsive.
EDIT: It is no longer called layoutopt, it's called lint. Check your ~/android-sdk/tools/
I have once faced a situation where a fragment was really slow.
I am just predicting that your fragment has some kind of alpha and it is drawn on a 'heavy' activity.
The conclusion is that each time you are setting the text of a textview your whole view hierarchy is being invalidated.
It seems that fragments have this flaw. Anyway, use some layout instead of the fragment and check if it remains 'slow'.
ADDITION: A wrap_content textview will cause much more delay after a setText than a fill_parent textview.
You're likely running into slowdowns due to layout management with TableLayout and TextView. Every time you update text in one of those, a large amount of view measuring has to take place in order to put the characters in the right place on the screen. You should really just profile the app yourself using Traceview to find out. More information at: http://developer.android.com/tools/debugging/debugging-tracing.html
I've had the exact same issue you're seeing with the same type of layout (Fragment > TableLayout > Multiple TextViews). One way to test if your TableLayout/TextView setup is to blame is simply replace all that with a single TextView. That will probably run pretty well. Then put your 14 views into a FrameLayout or RelativeLayout. Even if they all overlap, you should still get decent performance, because it's the complexity of the TableLayout view measurements that's really causing slowdown.
As someone said you can use HardwareAccelerated but this is not a great solution, you will waste ram and cpu if you can't solve it in a different way. A solution probably more safety is to reduce the number of TextView. Try to reduce 14 to 7 and it will go twice faster. Usually is hard to do it but if you put the objects in a strategy position a pair of TextView one above other can be together if you make a TextView with two lines. And don't forget that findViewById is so expensive, if you will use a view object often find it one time and hold its reference.
Benchmarks are always useful for determining where slowness actually comes from, but I feel pretty confident suggesting that sending an Intent is probably much slower than updating 14 TextViews. Sending 10 Intents per second is a sign that you're Doing It Wrong (TM). This is just isn't what they're for.
Am I doing anything wrong, or is it just that I'm trying to do something very complex in terms of efficiency?
Updating 14 TextViews per second isn't inherently complex; you should be able to easily achieve this with a more appropriate application design. ASyncTask or Handler come to mind as possible tools, but it's hard to know what's best without knowing more about exactly what you're trying to do.
You can try to declare vars outside the loop :
public void actualizarTiempoJuego(long milis){
// Se recuperan los tiempos acumulados y se
// aumenta la cantidad pasada como parámetro
long mCurrentMilis1;
long mCurrentMilis2;
TextView1 t1;
TextView1 t2;
for(int i=0;i<7;++i) {
mCurrentMilis1 = mVectorMilis1.get(i);
mCurrentMilis2 = mVectorMilis2.get(i);
t1 = mListaTitularLayoutLocal.get(i);
t2 = mListaTitularLayoutVisitante.get(i);
t1.setText(String.value(milis + mCurrentMilis1));
t2.setText(String.value(milis + mCurrentMilis2));
}
}
And to setText() with mixed type, you can try setText("" + milis + mCurrentMilis2);
I am developing an app which instantiates a bunch of bitmap objects (e.g. buttons, which have cache bitmaps, so they don't have to get rendered again and again)
Now, I realised that when I run and start the app repeatedly on my huawei mobile device, I get an OutOfMemoryException at a point where the app tries to allocate some memory for the bitmaps.
So I guess it's the bitmaps which make trouble. I do know that there is a bitmap.recycle() method though.
Now my question: what is best practice to clean up memory?
Why isn't there some View method like View::onDestroy() which can be implemented for cleanup purpose?
EDIT: example
my "CirclyButton" (extends Button) class always draws a cached bitmap onDraw:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(this.getDefaultBitmap(), 0, 0, paint);
}
private Bitmap getDefaultBitmap(){
if(mBitmapDefault == null){
mBitmapDefault = Bitmap.createBitmap(8*radius, 8*radius, Config.ARGB_8888);
Canvas canvas = new Canvas(mBitmapDefault);
this.drawDefault(canvas);
return mBitmapDefault;
}
return mBitmapDefault;
}
So I guess this allocated data should be recycled somewhere...?
Views don't have an onDestroy method because views usually don't get destroyed, activities do. A view won't just be destroyed if nothing happens to its activity (Unless you inflate a different layout... That's not the case, right?), and if something happens to its activity, you do have a callback getting called.
If there is a recycle() method, make sure you call it. And remove all reference to memory taking objects in the onDestroy, i.e:
#Override
public void onDestroy() {
object1 = null;
object2 = null;
//...
}
So the GC can do its job. I had the same problem with the AdView of AdMob, although they did have a destroy method it didn't really help. But deleting my references of the view fixed the problem.
Provide more information about where are you using your bitmaps, i have some serious experience of working with images and saving memory.
For example in my app i have a list of some data, which display some bitmap in each row. I store my list in a fragment(for fragment support i use compatibility library), and i recycled my bitmaps on this fragment onDestroy method.
Later i decided to optimize my list, so i added scroll listener to my list and started recycling bitmaps, when they are scrolled off the screen.
How Do You Force A NinePatchDrawable to release the BitMap bytes it parsed out of 'res'?
As an Android developer I face pressure to control memory utilization in my games.
I work to control memory utilization by releasing resources as quickly as I can after they are no longer used. To this end, I maintain a list of all loaded resources and purge / release them from memory as soon as I am done with them.
My application uses a number of different types of graphical resources
BitMap
BitMapDrawable
Drawable
NinePatchDrawable
How do I release these objects right now?
BitMap : I use the "recycle()" method
BitMapDrawable : I use the "getBitMap().recycle()" method
Drawable : I set these to null (not working)
NinePatchDrawable : I set these to null (not working)
What have you tried?
You cannot "getBitmap()" a NinePatchDrawable
You cannot convert a NinePatchDrawable to a BitMapDrawable (even if they are both Bitmap based Drawables)
There seems to be a way to parse the PNG yourself, feeding the bytes into NinePathDrawable yourself -- this might get me to a point where I can just "recycle()" the underlying BitMap myself, but that seems like I'm reinventing the wheel (http://code.google.com/p/android/issues/detail?id=13542)
My Current Rules:
Never use #drawable/ in XML
Never android:background in XML
Never android:src in XML
To be able to use: #drawable/, android:background, android:src in xml, you can always use custom classes.
So instead of
<ImageView android:src="#drawable/bg" />
you can use:
<com.myPackage.CustomImageView android:src="#drawable/bg" />
Than in the CustomImageView in the constructor you can get the references to your xml attributes:
private Drawable bg2;
private Drawable bg1;
public void CustomImageView(Context context, Attrs attrs)
{
super(context, attrs);
// Use this to get references to your xml attributes
int resourceId = attrs.getAttributeResourceValue("android", "src", 0);
bg1 = getResources().getDrawable(resourceId);
// Or for the 'background' attribute
resourceid = attrs.getAttributeResourceValue("android", "background", 0);
bg2 = getResources().getDrawable(resourceId);
// Now you can recycle() your 'bg' whenever you're done
}
This way, you can extract references from your xml. And recycle() them when you think it's appropriate
I too am frustrated by the outofmemory bug. My application was throwing an outofmemory error whenever the user went from one activity to another. Setting my drawables to null and calling System.gc() didn't work, neither did recycling my bitmapDrawables with getBitMap().recycle(). Android would continue to throw the outofmemory error with the first approach, and it would throw a canvas error message whenever it tried using a recycled bitmap with the second approach.
I took an even third approach. I set all views to null and the background to black. I do this cleanup in my onStop() method. This is the method that gets called as soon as the activity is no longer visible. The onDestroy() method might not get called. Also, if the cleanup is done in the onPause() method, users will get a black screen before moving onto the next screen.
To prevent a black screen from occurring if the user presses the back button on the device, I then reload the activity in the onRestart() method by calling the startActivity(getIntent()) and then finish() methods.
Note: it's not really necessary to change the background to black.