android - EditText 和 DialogFragment 导致 OutOfMemory 崩溃

标签 android memory-leaks android-edittext android-dialogfragment

我遇到了恼人的内存泄漏和崩溃,这似乎是一个平台错误。我正在尝试调试/另一个/内存问题,但我不断发现与 EditText 相关的错误,从而保留了我的 View 以及其中的 View 。

给定一个 DialogFragment、一个带有 EditText 和 UI Monkey 的布局,我可以使以下简单的应用程序因内存不足异常而崩溃。

我已经在 x86 模拟器 17 - 19 上进行了测试,最新更新截至 2014 年 3 月 21 日,堆大小为 16MB。在 4.4.2 的 Nexus 4 上,我看到内存泄漏在五分钟内不断增加,但从未停留足够长的时间以使其崩溃,但在查看 N4 的内存转储时,显示了相同的泄漏。/p>

当按下选项菜单时,Activity 将打开一个 DialogFragment:

package org.example.edittextoom;

import android.app.Dialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;

public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        DialogFragment df = new Df();
        df.show(getSupportFragmentManager(), "dialogtag");
        return true;
    }

    public static class Df extends DialogFragment {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            Dialog onCreateDialog = super.onCreateDialog(savedInstanceState);
            return onCreateDialog;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            // Using inflator from app context instead to attempt to avoid the issue with EditTexts holding onto Activity's
            View view = LayoutInflater.from(
                    getActivity().getApplicationContext()).inflate(
                    R.layout.dialog, container, false);
                    // Since disabling no suggestions is suppose to prevent the edit text leak
                    EditText et = (EditText) view.findViewById(R.id.editText1);
                    int it = et.getInputType();
                    et.setInputType(it | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
                    it = et.getInputType();
            return view;
        }

    }

}

这两种布局都很简单。该对话有一个 EditText。

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

</RelativeLayout>

dialog.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:inputType="text|textNoSuggestions"
        />

</RelativeLayout>

让我们通过运行 UI Monkey 来查看内存泄漏情况。

adb shell monkey -p org.example.edittextoom --pct-nav 0 --pct-majornav 0 --pct-appswitch 0 800000

(如果您要运行此程序,请在屏幕尺寸最小的 x86 模拟器上运行,以提高速度。)

当它运行时,您还可以通过查看此处的第五列来确认它占用的内存量正在增加,大约需要一分钟左右(顺便说一句,它通常会在大约 56000 时崩溃):

$ adb shell ps | grep edittext
u0_a46    2727  794   177888 31596 ffffffff b7f34997 S com.example.edittextoom
$ adb shell ps | grep edittext
u0_a46    2727  794   177888 31784 ffffffff b7f3322a S com.example.edittextoom
$ adb shell ps | grep edittext
u0_a46    2727  794   177956 31852 ffffffff b7f34997 S com.example.edittextoom
$ adb shell ps | grep edittext
u0_a46    2727  794   178124 32064 ffffffff b7f3322a S com.example.edittextoom

然后它最终会崩溃(我发现大约事件 150,000 到 500,000)输出:

// CRASH: com.example.edittextoom (pid 13620)
// Short Msg: java.lang.OutOfMemoryError
// Long Msg: java.lang.OutOfMemoryError
// Build Label: generic_x86/sdk_x86/generic_x86:4.2/JOP40C/eng.android-build.20121231.103448:eng/test-keys
// Build Changelist: eng.android-build.20121231.103448
// Build Time: 1356921334000
// java.lang.OutOfMemoryError
//      at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
//      at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:502)
//      at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:355)
//      at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:785)
//      at android.content.res.Resources.loadDrawable(Resources.java:1965)
//      at android.content.res.Resources.getDrawable(Resources.java:660)
//      at com.android.internal.policy.impl.PhoneWindow.generateLayout(PhoneWindow.java:2832)
//      at com.android.internal.policy.impl.PhoneWindow.installDecor(PhoneWindow.java:2875)
//      at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:285)
//      at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:279)
//      at android.app.Dialog.setContentView(Dialog.java:482)
//      at android.support.v4.app.DialogFragment.onActivityCreated(DialogFragment.java:366)
//      at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1508)
//      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:947)
//      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
//      at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
//      at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
//      at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
//      at android.os.Handler.handleCallback(Handler.java:725)
//      at android.os.Handler.dispatchMessage(Handler.java:92)
//      at android.os.Looper.loop(Looper.java:137)
//      at android.app.ActivityThread.main(ActivityThread.java:5039)
//      at java.lang.reflect.Method.invokeNative(Native Method)
//      at java.lang.reflect.Method.invoke(Method.java:511)
//      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
//      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
//      at dalvik.system.NativeStart.main(Native Method)
//
** Monkey aborted due to error.
Events injected: 131816
:Sending rotation degree=0, persist=false
:Dropped: keys=1534 pointers=13377 trackballs=0 flips=0 rotations=0
## Network stats: elapsed time=308525ms (0ms mobile, 0ms wifi, 308525ms not connected)
** System appears to have crashed at event 131816 of 800000 using seed 1393322223373

(有时模拟器会不断占用内存,并杀死应用程序,但您不会强制关闭对话,因此 UI Monkey 会进入循环,玩模拟器,直到再次启动应用程序,但不管怎样,应用程序占用这么多内存后就会被系统杀死。)

如果您停止 UI Monkey 中流,通过取消其进程,您可以检查 MAT 中的 hprof 以查看占用内存的内容(我退出应用程序并在查看 hprof 之前执行强制 gc),以查看它仅保留一个 MainPageActivity,但保留 63 个 MainPageActivity$Df 实例,而且这是在运行仅一分钟左右之后的结果。我在 Nexus 4 上查看内存转储时也得到同样的结果。

如果您列出对其中之一的传入引用,然后单击“GC 根路径”并排除弱引用和软引用,您会得到以下结果(与我所看到的所有其他 63 个引用相同):

enter image description here

但是,如果我将对话框中的 EditText 更改为带有 id 的 ProgressBar 或带有 id 的 TextView,则不会出现 MAT 中显示的崩溃或内存泄漏问题。所以我相信这与此有关EditText causing memory leak但是,所有解决方法都不起作用,既不使 EditText 的 InputConnection 为空,也不关闭建议(我在上面的示例中已完成)。如果我删除 EditText 的 id,我也设法摆脱泄漏,但随后我无法访问我的 EditText。

您是否见过类似的情况,是否找到了解决方法,或者您是否可以在我的代码中看到一些疏忽?

此问题目前导致我无法正确查找应用程序中的内存泄漏。

最佳答案

我也遇到了同样的问题。由于编辑器内的闪烁,对话框内的 EditText 会泄漏内存。这种情况发生在 Samsung Tab 10 4.4.2 上,但没有发生在 Nexus 7 4.4.2 上,这很奇怪。

解决这个问题的唯一方法是禁用 EditText 中的光标。使用 setCursorVisible(false)。如果您希望光标闪烁,可以在关闭对话框之前禁用光标,这样可以防止 Blinker 再次自行调度。

注意:rana 的建议显然是错误的。他根本不明白静态嵌套类和非静态内部类之间的区别。非静态内部类将保留对您的 Activity 的隐式引用,因此实际上您确实希望在此处使用静态嵌套类,这与在单独的文件中声明此类相同。

关于android - EditText 和 DialogFragment 导致 OutOfMemory 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22339458/

相关文章:

java - 键盘 View 键码不起作用

memory-leaks - 是否应该始终销毁 CUDA 事件和流?

c++ - 这是内存泄漏吗?

java - 如何使用 TextWatcher 检查特定字符串而不是单个字符?

android - 获取光标当前行的屏幕坐标 EditText

java - 使用 ViewHolder 会产生异常输出

java - 在Android中获取用户的 "other"Google联系人

c# - 当我的代码不存在时,我如何让我的代码创建一个 sqlite 数据库?

javascript - Javascript 中的绑定(bind)函数会导致内存泄漏吗?

android:singleline = true 不适用于edittext