在Android中使用ListView时内存泄漏

时间:2010-10-22 14:50:16

标签: android memory-leaks

我正在努力解决与ListView相关的内存泄漏问题。我创建了以下小程序,它表现出这种行为。

我所做的是创建2个LinearLayouts。第一个有Button和GListView控件。下面是GListView的代码,但它只是子类ListView,并实现了ListAdapter接口。创建GListView时,它会将其适配器设置为自身。

现在,当您按下按钮时,我切换到第二个LinearLayout。这种布局只有一个按钮。当您按下此按钮时,我使用新的GListView创建一个新的第一个布局并将其设置为活动视图。

运行程序,并在两个视图之间切换20次。然后调出DDMS并强制进行垃圾收集。然后转储内存,并使用Memory Analyzer,你会发现剩下21个GListView对象。也就是说,没有再连接任何东西的20个GListViews都没有被释放。

如果我进行内存转储并查看应该回收的其中一个GListViews,并使用Memory Analyzer列出命令引用,我会得到以下内容:

Class Name                                                              | Shallow Heap | Retained Heap 
-------------------------------------------------------------------------------------------------------
com.gabysoft.memoryleak.GListView @ 0x43e72270 Unknown                  |          672 |         3,528 
|- host android.view.View$ScrollabilityCache @ 0x43e72560               |           80 |           584 
|- this$0 android.widget.AbsListView$RecycleBin @ 0x43e72830            |           40 |           160 
|- mCallback android.graphics.drawable.StateListDrawable @ 0x43e728a8   |           64 |         1,464 
|- this$0 android.widget.AdapterView$AdapterDataSetObserver @ 0x43e730b8|           16 |            16 
-------------------------------------------------------------------------------------------------------

现在,如果我在GListView构造函数中注释掉'setAdapter(this)'函数并重复上述内容,我发现只剩下1个GListView。也就是说,在这种情况下,所有未使用的GListView都已正确回收。

有人建议我在我的GListView中创建一个私有类来处理ListAdapter接口,我试过了,但它没有帮助。我也尝试创建一个完全独立的公共类来处理ListAdapter,但是,唉,这似乎也不起作用。

肯定有一些方法可以让这些物品在任何地方不再使用时消失。 (这不是垃圾收集的全部内容吗?)

任何帮助将不胜感激。我真的把头发拉出来了。

感谢。

/*
 * Activity
 */

package com.gabysoft.memoryleak;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;

public class MemoryLeak extends Activity implements android.view.View.OnClickListener 
{
    LinearLayout ll2;
    boolean page2 = false;

    private LinearLayout CreateLayout()
    {
        LinearLayout ll = new LinearLayout(this);

        Button btn1 = new Button(this);
        ListView    lv    = new GListView(this);

        btn1.setText("Press");
        btn1.setLayoutParams(new LinearLayout.LayoutParams(100, 40));
        btn1.setOnClickListener(this);

        ll.addView(btn1);
        ll.addView(lv);

        return(ll);
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);

        CreateLayout();

        LinearLayout ll = CreateLayout();
        ll2 = new LinearLayout(this);

        Button btn2 = new Button(this);

        btn2.setText("Back");
        btn2.setLayoutParams(new LinearLayout.LayoutParams(100, 40));
        btn2.setOnClickListener(this);

        ll2.addView(btn2);

        setContentView(ll);
    }

    @Override
    public void onClick(View v) 
    {
        if (page2)
        {
            LinearLayout ll = CreateLayout();

            setContentView(ll);

            page2 = false;
        }
        else
        {
            setContentView(ll2);
            page2 = true;
        }
    }

}

/*
 * GListView
 */
package com.gabysoft.memoryleak;

import android.content.Context;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class GListView extends ListView implements ListAdapter
{
    Context m_context;
    DataSetObserver m_observer = null;

    public GListView(Context context) 
    {
        super(context);

        m_context    = context;

        setAdapter(this);

        setChoiceMode(CHOICE_MODE_SINGLE);
    }

    /*
     * ListAdapter 
     */


    @Override
    public boolean areAllItemsEnabled() 
    {
        return true;
    }

    @Override
    public boolean isEnabled(int position) 
    {
        return true;
    }

    @Override
    public int getCount() 
    {
        return(0);
    }    

    @Override
    public Object getItem(int position) 
    {
        return null;
    }

    @Override
    public long getItemId(int position) 
    {
        return(position);
    }

    @Override
    public int getItemViewType(int position) 
    {
        return 0;
    } 

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
    {
        TextView tv = new TextView(m_context);

        tv.setText("Item");

        return(tv);
    }

    @Override
    public int getViewTypeCount() 
    {
        return 1;
    }

    @Override
    public boolean hasStableIds() 
    {
        return false;
    }

    @Override
    public boolean isEmpty() 
    {
        return false;
    }

    @Override
    public void registerDataSetObserver(DataSetObserver observer) 
    {
        m_observer    = observer;
    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) 
    {
        m_observer    = null;
    }
}

1 个答案:

答案 0 :(得分:1)

您的GListView个对象正在被垃圾收集。最少,当从DDMS完成手动GC时,会使用finalize()调用它们。不幸的是,DDMS HPROF文件似乎与我的jhat版本不兼容,而且我不是Eclipse用户,所以我没有准备好访问MAT。