Reputation: 765
An app I am working on is leaking memory. As far as I can tell I am doing everything suggested in: http://developer.android.com/resources/articles/avoiding-memory-leaks.html
I've cut my app down to a very simple app which does nothing but set the background image. Every time I do a screen orientation change the app leaks 30k-50k of memory.
So I am suspicious of the following:
SetContentView() and findViewById()
Do I need to do something in the onDestroy() related to these calls, to decouple them from the Activity?
Also I have a couple of questions. In onDestroy() I call setBackgroundResource(0). I believe if I do not do this, the Drawable for the background bitmap will maintain a callback to the view and this will cause a leak of the entire context. Is this true? Adding this call to the onDestroy() certainly seemed to make a big difference in the magnitude of the leaks. In the view constructors I try to remove some references to the activity by making the super() calls with the Application context, as opposed to the activity context. Does this actually provide this benefit or does it matter at all? Are there side effects to doing this that I should be aware of?
Code and XML follows: At this point I really don’t see why it should be leaking memory. Any enlightenment would be greatly appreciated.
MemLeak.java
package randombrand.MemLeak;
import randombrand.MemLeak.R;
import randombrand.MemLeak.MLView;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class MemLeak extends Activity {
private MLView mmlView;
private static final String strmlBundle = "Mem Leak";
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.memleak_layout);
mmlView = (MLView) findViewById(R.id.viewMemLeak);
try {
mmlView.Init(this);
SetBackgroundBitmap();
} catch (Exception ex) {
Log.e(strmlBundle, "Failed to launch Mem Leak." + ex);
this.finish();
}
if (icicle == null) {
mmlView.SetActive(true);
} else {
Bundle bundle = icicle.getBundle(strmlBundle);
if (bundle != null) {
mmlView.SetActive(true);
mmlView.invalidate();
} else {
mmlView.SetActive(false);
}
}
}
@Override
protected void onPause() {
super.onPause();
mmlView.SetActive(false);
}
@Override
protected void onResume() {
super.onResume();
mmlView.SetActive(true);
}
private void SetBackgroundBitmap() {
mmlView.setBackgroundResource(R.drawable.dark_background);
}
@Override
public void onDestroy() {
super.onDestroy();
mmlView.setBackgroundResource(0);
}
}
MLView.java
package randombrand.MemLeak;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
public class MLView extends View {
private static final long ltFpDrawDelay = 66;
Context mContextApp;
// true when we are not sleeping in the background
private boolean mfActive = false;
public MLView(Context context, AttributeSet aSet) {
super(context.getApplicationContext(), aSet);
}
public MLView(Context context, AttributeSet aSet, int nStyle) {
super(context.getApplicationContext(), aSet, nStyle);
}
public void Init(MemLeak mLeak) {
mContextApp = mLeak.getApplicationContext();
SetActive(true);
}
public void Update() {
mRedrawHandler.sleep(ltFpDrawDelay);
}
public void SetActive(boolean fActive) {
mfActive = fActive;
if (fActive) Update();
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
private RedrawHandler mRedrawHandler = new RedrawHandler();
class RedrawHandler extends Handler {
@Override
public void handleMessage(Message msg) {
MLView.this.Update();
MLView.this.invalidate();
}
public void sleep(long ltMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), ltMillis);
}
}
}
memleak_layout.java
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<randombrand.MemLeak.MLView
android:id="@+id/viewMemLeak"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="randombrand.MemLeak"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="5" />
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:debuggable="true">
<activity android:name="MemLeak"
android:launchMode="singleInstance"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Upvotes: 3
Views: 1593
Reputation: 33509
jbww,
I was noticing similar behavior in some of my Activities so I added
android:configChanges="keyboardHidden|orientation"
to the Activity
in AndroidManifest.xml
then reloaded the necessary information by overriding onConfigurationChanged()
.
I no longer noticed the heap grow each time I rotated the device.
I hope this works for you too.
Upvotes: 1