【问题标题】:Android memory leak using ActionBar and Spinner navigation使用 ActionBar 和 Spinner 导航的 Android 内存泄漏
【发布时间】:2012-03-05 09:03:30
【问题描述】:

我的代码在方向更改时出现大量内存泄漏。旋转手机一次或两次后堆已满。我试图追踪泄漏两天了,但没有成功。

应用程序使用 SDK 15(最少 14 个)。

MainActivity 显示一个带有列表导航的 ActionBar (NAVIGATION_MODE_LIST)。导航微调器会替换显示的片段。

代码有效。

ListNavigationItem,其中包含实例化 Fragments 所需的信息:

public class ListNavigationItem
{
    public final String fragmentClass;
    public final String title; // Used as fragment tag and as title

    public ListNavigationItem(String fragmentClass, String title)
    {
        this.fragmentClass = fragmentClass;
        this.title = title;
    }

    @Override
    public String toString()
    {
        return title;
    }
}

最后是我的(简化版)MainActivity

public class MainActivity extends Activity implements OnNavigationListener
{
    private MyApplication app;
    private ArrayAdapter<ListNavigationItem> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        app = (MyApplication) getApplicationContext();

        ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        actionBar.setDisplayShowTitleEnabled(false);
        actionBar.setDisplayShowHomeEnabled(false);

        adapter = new ArrayAdapter<ListNavigationItem>(this, android.R.layout.simple_spinner_dropdown_item);
        adapter.add(new ListNavigationItem(FirstFragment.class.getName(), "First"));
        adapter.add(new ListNavigationItem(SecondFragment.class.getName(), "Second"));

        actionBar.setListNavigationCallbacks(adapter, this);
    }

    public boolean onNavigationItemSelected(int itemPosition, long itemId)
    {
        ListNavigationItem item = adapter.getItem(itemPosition);
        if (item == null)
            return false;

        Fragment f = getFragmentManager().findFragmentByTag(item.title);

        if (f == null)
            f = Fragment.instantiate(this, item.fragmentClass);
        else if (f.isAdded())
            return true;

        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        fragmentTransaction.replace(R.id.main_content, f, item.title);
        fragmentTransaction.commit();

        return true;
    }

    @Override
    protected void onDestroy()
    {
        adapter = null; // Do I need this?
        app = null;
        super.onDestroy();
    }
}

清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="at.company.app"
    android:versionCode="1"
    android:versionName="1.0">

    <uses-sdk android:minSdkVersion="14" />
    <uses-sdk android:targetSdkVersion="15" />

    <application
        android:name=".Application"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">

        <activity
            android:name=".MainActivity"
            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>

MAT 堆分析表明,android.content.res.Resources 类占用了 57% 的可用内存,android.graphics.Bitmap 占用了 12%。然而,我的片段只包含一个简单的TextView

MAT 直方图显示MainActivity 有 4 个对象(我假设是实例?)。此外,logcat 显示在方向更改 (GC_CONCURRENT) 后堆经常被清空。

官方开发指南不鼓励使用android:configChanges="orientation"。 (Source)

我对 Java 和 Android 平台比较陌生,如果能提供任何帮助,我将不胜感激。

【问题讨论】:

    标签: android memory-leaks android-fragments android-4.0-ice-cream-sandwich


    【解决方案1】:

    内存泄漏的一个常见原因是在上下文引用中引用Activity 而不是Application。使用this(例如adapter = new ArrayAdapter&lt;ListNavigationItem&gt;(this)可能会导致内存泄漏,如question 中所述,因此请尝试将其替换为getApplicationContext() 以引用Application 上下文。

    【讨论】:

    • 感谢您的回答!下次我再次使用该应用程序时,我会验证它。
    • this 更改为 getApplicationContext(),它仍然会导致 GC 每 3-5 秒运行一次。不过,它似乎并不影响性能。
    • GC 运行并不意味着你有泄漏。相反,内存泄漏意味着内存中有对象引用它们,这阻止 GC 运行。所以堆不断填充,你得到内存异常。检查你的内存是否持续增长,这表明你是否有泄漏(不是 GC)。
    【解决方案2】:

    我不知道为什么,如何,或者这是否是我的问题。但是当我删除片段事务的 setTransition 时,我似乎停止获得“增长堆”输出。试试看?

    【讨论】:

    • 这是评论,不是答案
    【解决方案3】:

    这将取决于您的清单配置,但要进行测试,您可以完成 onConfigurationChanged.. 否则它只会创建一个新活动而不会破坏旧活动..

    【讨论】:

    • 感谢您的回答。但是我不明白,我应该在哪个类中实现onConfigurationChanged() 方法?我应该测试什么?据我了解,没有android:configChanges 属性就不会调用该方法。
    • 它在屏幕旋转时被调用..你在活动中实现它..只是为了调查btw..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    • 2011-09-03
    • 2020-10-24
    • 2014-12-11
    • 1970-01-01
    相关资源
    最近更新 更多