Reputation: 11309
I was just following this article on how to avoid memory leaks : android developer blog Following is the code snippet used :
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);
}
It is said that the drawable has a callback reference to textview (and indirectly to the activity); which will be preserved on rotation - and hence memory leak.
My query is that won't the drawable's callback be reset on rotation - it would get hold of the new textview (which will hold the new context).. hence allowing the previous instances of textview/context to be GC'ed.
EDIT : The answers I get are on how to "solve" the issue - I am not looking for that ! Please re-read the query. I am adding more details. When activity is launched, the references are :
Drawable1 -> TextView1 -> Activity1
When rotated, Activity1 and TextView1 are destroyed but not Drawable1
Drawable1 -> TextView2 -> Activity2
This means, Activity1 and TextView1 are free to be GC'ed - as no other object is having a reference to them. So what is leaking ?
Am I wrong in this understanding ? Or is it that the Drawable can have multiple views as callbacks ? (Looking at the source code, I dont see a list of callbacks on Drawable).
Upvotes: 12
Views: 2525
Reputation: 1974
If you rotate the device, the same MyActivity
class (or whatever name you gave it) is recreated, the callback is overwritten and the leak exists until the next GC. The problem lies when you navigate to another activity, keeping a reference to the old one. Today, this is mitigated because the setCallback
now stores the callback in a WeakReference
as you can see in current Drawable code, but it was a strong reference once (search for setCallback(Callback cb)
). Anyway, you're right, if you just look into one activity, the callback will be reset after rotating.
(edit, paragraph added):
For example: MainAcivity@1 is the first instance. When you rotate, it's destroyed and a new MainActivity@2 is created. At first, there's a leak, but as soon as sDrawable
is reassigned, MainActivity@1 is free to be collected and there's no problem. Now suppose that, instead of rotating, you navigate away to SecondActivity. Now, sDrawable
is just for MainActivity
and still holds a reference to MainActivity@2, so it leaks.
See this code:
package com.douglasdrumond.leaky;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.widget.TextView;
public class MainActivity extends Activity {
private static Drawable sBackground;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView label = new TextView(this);
System.gc();
long memory = Runtime.getRuntime().totalMemory()
- Runtime.getRuntime().freeMemory();
label.setText("Memory: " + memory / 1024f);
if (sBackground == null) {
sBackground = getResources().getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
}
Clearly, rotating doesn't increase memory usage.
Upvotes: 9