Утечка памяти в очень простом приложении

Приложение, над которым я работаю, теряет память. Насколько я могу судить, я делаю все, что предлагается в: http://developer.android.com/resources/articles/avoiding-memory-leaks.html

Я сократил свое приложение до очень простого приложения, которое ничего не делает, кроме установки фонового изображения. Каждый раз, когда я меняю ориентацию экрана, приложение теряет 30-50 тысяч памяти.

Поэтому у меня вызывает подозрения следующее:

SetContentView() и найтиViewById()

Нужно ли мне что-то делать в onDestroy(), связанном с этими вызовами, чтобы отделить их от Activity?

Также у меня есть пара вопросов. В onDestroy() я вызываю setBackgroundResource(0). Я считаю, что если я этого не сделаю, Drawable для растрового изображения фона будет поддерживать обратный вызов представления, и это вызовет утечку всего контекста. Это правда? Добавление этого вызова в onDestroy() определенно сильно повлияло на величину утечек. В конструкторах представлений я пытаюсь удалить некоторые ссылки на действие, выполнив вызовы super() с контекстом приложения, а не с контекстом действия. Дает ли это на самом деле такое преимущество или вообще имеет значение? Есть ли побочные эффекты, о которых я должен знать?

Далее следует код и XML: на данный момент я действительно не понимаю, почему должна происходить утечка памяти. Любое просветление будет высоко оценено.

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>

person jbww    schedule 18.03.2011    source источник
comment
Пожалуйста, просмотрите свои предыдущие вопросы и отметьте лучший ответ как правильный, установив флажок рядом с ним.   -  person Cheryl Simon    schedule 18.03.2011
comment
Я знаю, что это старый вопрос, но поможет ли обнуление представления в onDestroy? как в mmlView=null;. Кроме того, почему вы используете context.getApplicationContext() в своем представлении? Так должно быть?   -  person Mr_and_Mrs_D    schedule 25.11.2013


Ответы (1)


джбвв,

Я заметил подобное поведение в некоторых своих действиях, поэтому я добавил

android:configChanges="keyboardHidden|orientation"

в Activity в AndroidManifest.xml, затем перезагрузил необходимую информацию, переопределив onConfigurationChanged().

Я больше не замечал, что куча растет каждый раз, когда я поворачиваю устройство.

Я надеюсь, что это сработает и для вас.

person Will Tate    schedule 18.03.2011
comment
Большое спасибо, вроде сработало. Все, что я сделал, это добавил изменения в манифест. На самом деле я не переопределял onConfigurationChanged(), так как моему приложению не нужно ничего делать, чего оно уже не делает в onCreate(). - person jbww; 19.03.2011
comment
Может ли кто-нибудь сказать мне, где публикуются сообщения о предполагаемых ошибках в Android? - person jbww; 19.03.2011