【问题标题】:EditText causing memory leakEditText 导致内存泄漏
【发布时间】:2023-12-27 12:18:01
【问题描述】:

简介:

我有一个具有以下结构的应用程序: 顶部操作栏 (ActionBarSherlock) 下方的 ViewPagerIndicator(用于标签) ViewPager(托管片段)

我有一个问题,我的一个片段导致了相当大的内存泄漏。我将问题缩小到以下情况:

导致泄漏的片段除了在其onCreateView 方法中膨胀布局外什么也不做。这是通过以下方式完成的:

return inflater.inflate(R.layout.filter_auctions_fragment, container, false);

这里没有什么不寻常的。

布局文件中仅包含一个ScrollViewLinearLayout 和两个EditTexts(包括更常见的东西,但我将问题缩小到仅这些视图以使其简单)。

现在用于添加片段的代码: mTabsAdapter.addTab(tabName, ProblematicFragment.class);

mTabsAdapterTabsAdapter 的一个实例,该类扩展了支持库的FragmentPagerAdapter。这是相当标准的,所以我不包括来源以使这个问题尽可能简短。

现在有趣的部分:

当我来回旋转我的设备几次时,堆会发生这种情况:

12-28 12:26:27.180: D/dalvikvm(18841): GC_CONCURRENT freed 530K, 7% free 10701K/11436K, paused 4ms+7ms, total 58ms
12-28 12:26:27.180: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 24ms
12-28 12:26:28.270: D/dalvikvm(18841): GC_CONCURRENT freed 737K, 8% free 11048K/11964K, paused 4ms+5ms, total 53ms
12-28 12:26:29.510: D/dalvikvm(18841): GC_CONCURRENT freed 789K, 8% free 11464K/12436K, paused 5ms+5ms, total 42ms
12-28 12:26:30.640: D/dalvikvm(18841): GC_CONCURRENT freed 888K, 9% free 11919K/12984K, paused 4ms+5ms, total 52ms
12-28 12:26:31.810: D/dalvikvm(18841): GC_CONCURRENT freed 903K, 8% free 12421K/13500K, paused 3ms+8ms, total 58ms
12-28 12:26:33.800: D/dalvikvm(18841): GC_CONCURRENT freed 1092K, 9% free 13005K/14272K, paused 4ms+6ms, total 59ms
12-28 12:26:33.800: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 20ms
12-28 12:26:36.000: D/dalvikvm(18841): GC_CONCURRENT freed 1355K, 11% free 13518K/15048K, paused 3ms+8ms, total 74ms
12-28 12:26:36.000: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 19ms
12-28 12:26:38.110: D/dalvikvm(18841): GC_CONCURRENT freed 1450K, 11% free 14106K/15720K, paused 3ms+11ms, total 72ms
12-28 12:26:40.450: D/dalvikvm(18841): GC_CONCURRENT freed 1530K, 11% free 14807K/16516K, paused 2ms+15ms, total 75ms
12-28 12:26:40.450: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 29ms
12-28 12:26:43.030: D/dalvikvm(18841): GC_CONCURRENT freed 1682K, 11% free 15591K/17452K, paused 3ms+10ms, total 66ms
12-28 12:26:43.030: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 32ms

显然,内存泄漏。 是的,我知道它会导致从头开始重新创建 Activity,这就是我想要的,因为我有不同的横向和纵向模式布局。尽管如此,它不应该导致内存泄漏。

我找到了这个问题的根源。就是我之前提到的两个EditText。一旦我从布局中删除它们并进行相同的测试(来回旋转)。这些是我收到的 GC 消息:

12-28 12:21:41.270: D/dalvikvm(17934): GC_CONCURRENT freed 534K, 7% free 10853K/11576K, paused 3ms+7ms, total 44ms
12-28 12:21:42.560: D/dalvikvm(17934): GC_CONCURRENT freed 818K, 9% free 11113K/12108K, paused 11ms+9ms, total 95ms
12-28 12:21:44.680: D/dalvikvm(17934): GC_CONCURRENT freed 1036K, 10% free 11313K/12528K, paused 3ms+6ms, total 54ms
12-28 12:21:44.680: D/dalvikvm(17934): WAIT_FOR_CONCURRENT_GC blocked 15ms
12-28 12:21:47.420: D/dalvikvm(17934): GC_CONCURRENT freed 1089K, 10% free 11510K/12780K, paused 2ms+6ms, total 79ms
12-28 12:21:47.420: D/dalvikvm(17934): WAIT_FOR_CONCURRENT_GC blocked 39ms
12-28 12:21:50.200: D/dalvikvm(17934): GC_CONCURRENT freed 1317K, 12% free 11461K/12956K, paused 4ms+13ms, total 84ms
12-28 12:21:53.210: D/dalvikvm(17934): GC_CONCURRENT freed 1629K, 14% free 11148K/12956K, paused 3ms+7ms, total 47ms
12-28 12:21:55.580: D/dalvikvm(17934): GC_CONCURRENT freed 1056K, 13% free 11302K/12956K, paused 4ms+7ms, total 59ms
12-28 12:21:57.280: D/dalvikvm(17934): GC_CONCURRENT freed 1306K, 14% free 11200K/12956K, paused 5ms+5ms, total 82ms
12-28 12:21:59.420: D/dalvikvm(17934): GC_CONCURRENT freed 1035K, 12% free 11408K/12956K, paused 3ms+7ms, total 55ms
12-28 12:22:01.990: D/dalvikvm(17934): GC_CONCURRENT freed 1392K, 13% free 11352K/12956K, paused 4ms+9ms, total 54ms
12-28 12:22:01.990: D/dalvikvm(17934): WAIT_FOR_CONCURRENT_GC blocked 30ms

这就是我想看到的!

为什么!?

有人能告诉我为什么会这样吗?我想补充一点,我不会在我的应用程序的任何地方保留对这些 EditText 对象的引用(我通常会这样做,但即使我出于测试目的删除了所有这些对象,泄漏仍然会发生)。

奖金 - MAT 泄露截图:

如您所见,Fragment 和 Activity 有 16 个实例,而应该只有一个。

编辑:

我注意到,当我在不同的活动中手动添加片段(使用FragmentManager.beginTransaction())时,不会发生泄漏!!!我现在完全糊涂了……

EDIT2:

删除EditTexts 的android:id 属性修复了它...但是现在它们几乎没用了...

【问题讨论】:

  • 您没有折叠打开 MAT 显示屏的 originalText 部分以查看额外副本的来源。请参阅屏幕截图底部的第三行。
  • 也许这个 SO 讨论是相关的 - *.com/questions/8497965/…
  • 我也有同样的问题,你找到解决办法了吗?
  • 你在设备上测试过 api > 11
  • 您是否将EditTextActivity 传递给另一个类的方法或构造函数?

标签: android memory memory-management memory-leaks heap-memory


【解决方案1】:

我找到了适合我需要的解决方案。

我将问题追踪到widget.EditableInputConnection。我认为在建议系统中进行查找。它还负责保持我的活动,从而导致内存泄漏。

我不需要这些建议,所以我想关闭它。然而,事实证明这很困难。 EditText.setInputType 不起作用,无论是在 xml 还是代码中。

我最终做了以下事情。奇迹发生在onCreateInputConnection()

public class MyEditText extends TextView {
    public MyEditText(Context context) {
        this(context, null);
    }

    public MyEditText(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.editTextStyle);
    }

    public MyEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        return null;
    }

    @Override
    protected boolean getDefaultEditable() {
        return true;
    }

    @Override
    protected MovementMethod getDefaultMovementMethod() {
        return ArrowKeyMovementMethod.getInstance();
    }

    @Override
    public Editable getText() {
        return (Editable) super.getText();
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(text, BufferType.EDITABLE);
    }

    /**
     * Convenience for {@link Selection#setSelection(Spannable, int, int)}.
     */
    public void setSelection(int start, int stop) {
        Selection.setSelection(getText(), start, stop);
    }

    /**
     * Convenience for {@link Selection#setSelection(Spannable, int)}.
     */
    public void setSelection(int index) {
        Selection.setSelection(getText(), index);
    }

    /**
     * Convenience for {@link Selection#selectAll}.
     */
    public void selectAll() {
        Selection.selectAll(getText());
    }

    /**
     * Convenience for {@link Selection#extendSelection}.
     */
    public void extendSelection(int index) {
        Selection.extendSelection(getText(), index);
    }

    @Override
    public void setEllipsize(TextUtils.TruncateAt ellipsis) {
        if (ellipsis == TextUtils.TruncateAt.MARQUEE) {
            throw new IllegalArgumentException("EditText cannot use the ellipsize mode "
                    + "TextUtils.TruncateAt.MARQUEE");
        }
        super.setEllipsize(ellipsis);
    }
}

诀窍在于拒绝 InputConnection。这将删除建议并消除内存泄漏。

希望这对您有所帮助..

【讨论】:

  • 魔术!请问您用什么版本的 Android 测试过这个?
  • 我无法让它在我测试过的任何东西上工作,模拟器 17、18、19 和 N4。
  • 这是针对 Android Lollipop 及以下版本的 EditText Out Of Memory(内存泄漏)的最佳解决方案。关于这个问题有很多讨论,但这是唯一可行的解​​决方法。由于这个问题,人们甚至决定完全放弃棒棒糖。给@aslakjo 买杯啤酒! OP 如果您尝试过,请接受答案。
  • 存在内存泄漏的 Android 6 应用,由 LeakCanary link 在 android.support.v7.widget.AppCompatEditText 的实例中发现,因为在 View.mContext 中引用了已销毁的 Activity。看来这个解决方案有效,不再泄漏。
【解决方案2】:

我在使用三星 Galaxy S3 时遇到了完全相同的问题。

solution from @aslakjo 对我不起作用。我已经禁用了建议

最后,我将 XML 布局中的 EditText 替换为 android:tagandroid:id。现在它工作正常,我包含在 viewpager 中的片段在不再可见时被正确地垃圾收集,不要问我为什么。

此设备可能会根据 id 的存在注册某些内容。

【讨论】: