EditText和DialogFragment导致OutOfMemory崩溃

new*_*our 2 android memory-leaks android-edittext android-dialogfragment

我遇到了令人讨厌的内存泄漏和崩溃,这似乎是一个平台错误.我正在尝试调试/另一个/内存问题,但我一直发现这个与EditText相关的错误,保持我的观点以及那些内容.

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

我已经在x86模拟器17-19上进行了测试,截至2014年3月21日的最新更新为16MB.在4.4.2上的Nexus 4上,我看到内存泄漏在五分钟内增加,增加和增加,但从未让它长到足以让它崩溃,但是在查看N4的内存转储时会显示相同的泄漏.

按下选项菜单时,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;
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

这两种布局很简单.对话框有一个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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

adb shell monkey -p org.example.edittextoom --pct-nav 0 --pct-majornav 0 --pct-appswitch 0 800000
Run Code Online (Sandbox Code Playgroud)

(如果您要运行此程序,请在屏幕尺寸最小的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
Run Code Online (Sandbox Code Playgroud)

然后它最终会崩溃(我发现的事件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
Run Code Online (Sandbox Code Playgroud)

(有时模拟器不断占用内存,并杀死应用程序,但你没有强制关闭对话,所以UI Monkey进入循环,玩模拟器直到它再次启动应用程序,但应用程序被杀死无论如何,在占用了这么多内存后由系统.)

如果你停止UI monkey mid流程,通过取消它的过程,你可以检查MAT中的hprof以查看占用内存的内容(我退出应用程序并在查看hprof之前执行强制gc),看看它只保留一个MainPageActivity但是63个实例的MainPageActivity $ Df,这只是运行一分钟左右.我看到Nexus 4上的内存转储也是一样的.

如果列出对其中一个的传入引用,并单击Path To GC Roots并排除弱引用和软引用,则会得到此结果(与我所看到的所有其他63个引用相同):

在此输入图像描述

但是,如果我将对话框中的EditText更改为带有id或带有id的TextView的ProgressBar,我不会发生此崩溃或MAT中显示的内存泄漏.因此我认为它与此EditText相关会导致内存泄漏错误,但是,没有任何解决方法可以工作,不会使EditText的InputConnection无效,也不会关闭建议(我在上面的示例中已经完成).如果我删除EditText的id,我也设法摆脱泄漏,但是我无法访问我的EditTexts.

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

此问题目前阻止我在我的应用程序中正确查找内存泄漏.

小智 5

我遇到了同样的问题.由于编辑器内部的Blink,在Dialog中有一个EditText会泄漏内存.这种情况发生在三星Tab 10 4.4.2上,但不会发生在Nexus 7 4.4.2上,这很奇怪.

解决它的唯一方法是在EditText中禁用游标.使用setCursorVisible(false).如果您希望光标闪烁,可以在关闭对话框之前禁用光标,这可以防止Blinker再次自行安排.

注意:拉纳的建议显然是错误的.他根本不了解静态嵌套类和非静态内部类之间的区别.非静态内部类将保存对您的活动的隐式引用,因此实际上您希望在此处使用静态嵌套类,即SAME在单独的文件中声明此类.