Android: AlertDialog вызывает утечку памяти

Мое приложение показывает AlertDialog с ListView внутри. Все работало нормально, плюшка, потом я решил проверить это на утечку памяти. После запуска приложения в течение некоторого времени я открыл MAT и создал отчет о подозрительных утечках. МАТ обнаружил несколько подобных утечек:

Один экземпляр "com.android.internal.app.AlertController$RecycleListView", загруженный "‹загрузчиком системных классов>", занимает ...

Я потратил много времени на поиски причины этой утечки. Обзор кода мне не помог, и я начал гуглить. Вот что я нашел:

Проблема 5054: AlertDialog, по-видимому, вызывает утечку памяти через сообщение в очереди сообщений

Решил проверить воспроизводится этот баг или нет. Для этого я создал небольшую программу, состоящую из двух активностей. MainActivity — точка входа. Он содержит только кнопки, которые запускают LeakedActivity. Последний просто показывает AlertDialog в своем методе onCreate(). Вот код:

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        findViewById(R.id.button).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(
                    new Intent(MainActivity.this, LeakedActivity.class));
            }
        });
    }
}

public class LeakedActivity extends Activity {
    private static final int DIALOG_LEAK = 0;

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

        if (savedInstanceState == null) {
            showDialog(DIALOG_LEAK);
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        if (id == DIALOG_LEAK) {
            return new AlertDialog.Builder(this)
                .setTitle("Title")
                .setItems(new CharSequence[] { "1", "2" },
                    new OnClickListener() {
                        private final byte[] junk = new byte[10*1024*1024];

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // nothing
                        }
                    })
                .create();
        }
        return super.onCreateDialog(id);
    }
}

MAT сообщает об утечке этого приложения com.android.internal.app.AlertController$RecycleListView каждый раз, когда AlertDialog закрывается, а LeakedActivity завершается.

Я не могу найти ошибку в этой маленькой программе. Это выглядит как очень простой случай использования AlertDialog, и он должен работать хорошо, но, похоже, это не так. Поэтому я хотел бы знать, как избежать утечек памяти при использовании AlertDialogs с элементами. И почему эта проблема до сих пор не решена? Заранее спасибо.


person Michael    schedule 16.08.2011    source источник


Ответы (4)


(12 февраля 2012 г.): см. ОБНОВЛЕНИЕ ниже.

Эта проблема на самом деле вызвана не AlertDialog, а скорее связана с ListView. Вы можете воспроизвести ту же проблему, используя следующее действие:

public class LeakedListActivity extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Use an existing ListAdapter that will map an array
    // of strings to TextViews
    setListAdapter(new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, mStrings));
    getListView().setOnItemClickListener(new OnItemClickListener() {
        private final byte[] junk = new byte[10*1024*1024];
        @Override
        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                long arg3) {
        }
    });     
}
    private String[] mStrings = new String[] {"1", "2"};
}

Поверните устройство несколько раз, и вы получите OOM.

У меня нет времени, чтобы узнать больше о настоящей причине (я знаю, что происходит, но не понимаю, почему это происходит; может быть ошибка или задумано). Но вот один обходной путь, который вы можете сделать, по крайней мере, чтобы избежать OOM в вашем случае.

Во-первых, вам нужно сохранить ссылку на просочившийся AlertDialog. Вы можете сделать это в onCreateDialog(). Когда вы используете setItems(), AlertDialog внутри создаст файл ListView. И когда вы устанавливаете onClickListener() в своем вызове setItems(), внутренне он будет назначен на ListView onItemClickListener().

Затем в просочившейся активности onDestroy() установите для AlertDialog ListView onItemClickListener() значение null, что освободит ссылку на прослушиватель и сделает любую память, выделенную в этом прослушивателе, подходящей для GC. Таким образом, вы не получите OOM. Это всего лишь обходной путь, и реальное решение должно быть включено в файл ListView.

Вот пример кода для вашего onDestroy():

@Override
protected void onDestroy() {
    super.onDestroy();
    if(leakedDialog != null) {
            ListView lv = leakedDialog.getListView();
            if(lv != null)  lv.setOnItemClickListener(null);
    }
}

ОБНОВЛЕНИЕ (12 февраля 2012 г.): после дальнейшего изучения эта проблема на самом деле связана не с ListView и не с OnItemClickListener, а с тем фактом, что GC не происходит немедленно и требуется время, чтобы решить, какой объекты подходят и готовы к сборке мусора. Попробуй это:

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // this will create reference from button to 
        // the listener which in turn will create the "junk"
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            private byte[] junk = new byte[10*1024*1024];
            @Override
            public void onClick(View v) {
                // do nothing
            }
        });
    }
}

Поверните пару раз, и вы получите OOM. Проблема в том, что после поворота junk все еще сохраняется, потому что GC еще не произошло и не может произойти (если вы используете MAT, вы увидите, что это junk все еще сохраняется слушателем кнопки глубоко внизу от GCroot, и Сборщику мусора потребуется время, чтобы решить, подходит ли этот junk и может ли он быть собран.) Но в то же время после ротации необходимо создать новый junk, а также из-за размера памяти (10 МБ на мусор), это вызовет ООМ.

Решение состоит в том, чтобы разорвать любые ссылки на прослушиватель (junkowner), в данном случае из кнопки, что практически превращает слушателя в GCroot с коротким путем к мусору и заставляет сборщик мусора быстрее принимать решение об освобождении мусорной памяти. Это можно сделать в onDestroy():

@Override
protected void onDestroy() {
    // this will break the reference from the button
    // to the listener (the "junk" owner)
    findViewById(R.id.button).setOnClickListener(null);
    super.onDestroy();
}
person Ricky Lee    schedule 20.08.2011
comment
Здорово. Не могли бы вы пометить свой вопрос как ответ, чтобы другие люди с похожей проблемой могли легко найти решение. - person Ricky Lee; 22.08.2011
comment
Обязательно, но чуть позже. Если никто не даст лучшего ответа в течение четырех дней, я приму ваш ответ и назначу за него награду. - person Michael; 22.08.2011
comment
Да, конечно. Я хочу сказать, что в stackoverflow есть много вопросов с хорошими ответами, но они не помечены как отвеченные (ответ не был принят). Для сообщества будет хорошо, если каждый, кто спрашивает, будет немного ответственным и отметит / примет ответ, чтобы он был более полезен для других с похожей проблемой. - person Ricky Lee; 22.08.2011
comment
Ваш ответ лучший, так что награда за вас. - person Michael; 26.08.2011
comment
Привет, в моей ситуации речь идет не о SetOnClickListiner, а о представлениях и ресурсах изображений, используемых в каждом ListViewItem. Как я могу решить эту проблему? - person Ata; 25.03.2012
comment
@Ata, я столкнулся с твоей проблемой, ты нашел какое-нибудь решение? - person user4o01; 09.02.2014
comment
@user4o01 user4o01, я не помню, так как он был слишком стар, но я думаю, вам нужно обнулить изображения, imgView.setImageBitmap(null); это приводит к удалению указателя загруженного изображения, и когда вызывается GC, он освобождает ресурс изображения. - person Ata; 10.02.2014

Не удается воспроизвести для Android 2.3.4 2.3.3. Я проверил ваш точный код на реальном устройстве, и, судя по тому, что я вижу в LogCat, размер кучи остается постоянным с течением времени. К сожалению, мне не удалось настроить hprof-conf мои дампы (ОШИБКА: ожидается версия 1.0.3)

08-19 08:41:58.026: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 13K, 50% free 2698K/5379K, external 83K/519K, paused 16ms
08-19 08:41:58.056: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1K, 43% free 3720K/6471K, external 83K/519K, paused 18ms
08-19 08:45:30.113: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1076K, 58% free 2723K/6471K, external 595K/1042K, paused 18ms
08-19 08:45:30.143: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3740K/6471K, external 507K/1019K, paused 19ms
08-19 08:45:35.869: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1087K, 58% free 2726K/6471K, external 595K/1019K, paused 18ms
08-19 08:45:35.899: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3744K/6471K, external 552K/1019K, paused 18ms
08-19 08:45:39.112: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2734K/6471K, external 595K/1019K, paused 17ms
08-19 08:45:39.152: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3752K/6471K, external 530K/1019K, paused 20ms
08-19 08:46:14.186: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1089K, 58% free 2736K/6471K, external 595K/1019K, paused 23ms
08-19 08:46:14.216: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 42% free 3755K/6471K, external 552K/1019K, paused 21ms
08-19 08:46:16.519: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1092K, 58% free 2736K/6471K, external 595K/1019K, paused 23ms
08-19 08:46:16.549: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3753K/6471K, external 530K/1019K, paused 22ms
08-19 08:47:15.686: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1089K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
08-19 08:47:15.716: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 3K, 42% free 3756K/6471K, external 561K/1019K, paused 18ms
08-19 08:48:01.391: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2736K/6471K, external 595K/1019K, paused 19ms
08-19 08:48:01.421: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3753K/6471K, external 530K/1019K, paused 19ms
08-19 08:48:09.409: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1089K, 58% free 2737K/6471K, external 758K/1019K, paused 18ms
08-19 08:48:09.449: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 42% free 3756K/6471K, external 561K/1019K, paused 21ms
08-19 08:48:11.771: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1076K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
08-19 08:48:11.811: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 530K/1019K, paused 20ms
08-19 08:48:13.653: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1089K, 58% free 2737K/6471K, external 595K/1019K, paused 18ms
08-19 08:48:13.683: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 3K, 42% free 3758K/6471K, external 561K/1019K, paused 19ms
08-19 08:48:15.785: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
08-19 08:48:15.825: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 530K/1019K, paused 19ms
08-19 08:48:18.227: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1088K, 58% free 2737K/6471K, external 595K/1019K, paused 19ms
08-19 08:48:18.257: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 42% free 3756K/6471K, external 552K/1019K, paused 20ms
08-19 08:49:06.575: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1075K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
08-19 08:49:06.605: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 530K/1019K, paused 17ms
08-19 08:49:09.668: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1097K, 58% free 2729K/6471K, external 595K/1019K, paused 18ms
08-19 08:49:09.708: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 43% free 3748K/6471K, external 552K/1019K, paused 20ms
08-19 08:49:12.440: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
08-19 08:49:12.470: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3753K/6471K, external 530K/1019K, paused 17ms
08-19 08:49:15.473: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1088K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
08-19 08:49:15.503: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 42% free 3756K/6471K, external 561K/1019K, paused 17ms
08-19 08:49:18.476: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1091K, 58% free 2737K/6471K, external 595K/1019K, paused 18ms
08-19 08:49:18.506: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 507K/1019K, paused 20ms
08-19 08:49:21.289: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1090K, 58% free 2737K/6471K, external 595K/1019K, paused 18ms
08-19 08:49:21.319: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 484K/996K, paused 20ms
08-19 08:51:43.307: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1071K, 58% free 2723K/6471K, external 595K/996K, paused 17ms
08-19 08:51:43.338: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed <1K, 43% free 3747K/6471K, external 595K/996K, paused 20ms
08-19 08:51:45.620: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1086K, 58% free 2729K/6471K, external 595K/974K, paused 18ms
08-19 08:51:45.660: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 7K, 43% free 3745K/6471K, external 462K/974K, paused 20ms
08-19 08:51:47.421: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1080K, 58% free 2738K/6471K, external 595K/974K, paused 17ms
08-19 08:51:47.452: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3755K/6471K, external 484K/974K, paused 19ms
08-19 08:52:56.949: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 27K, 58% free 2733K/6471K, external 83K/595K, paused 18ms
08-19 08:52:56.979: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed <1K, 42% free 3757K/6471K, external 83K/595K, paused 17ms
08-19 08:53:01.233: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1072K, 58% free 2727K/6471K, external 595K/1107K, paused 18ms
08-19 08:53:01.274: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 2K, 43% free 3749K/6471K, external 578K/1090K, paused 20ms
08-19 08:53:04.046: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1081K, 58% free 2740K/6471K, external 595K/1064K, paused 18ms
08-19 08:53:04.086: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3758K/6471K, external 530K/1042K, paused 20ms
08-19 08:53:25.948: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1090K, 58% free 2740K/6471K, external 595K/1042K, paused 19ms
08-19 08:53:25.978: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3758K/6471K, external 552K/1042K, paused 19ms
08-19 08:57:51.246: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1099K, 58% free 2753K/6471K, external 595K/1042K, paused 18ms
08-19 08:57:51.286: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 13K, 42% free 3764K/6471K, external 561K/1042K, paused 20ms
08-19 09:00:47.699: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1106K, 58% free 2731K/6471K, external 595K/1019K, paused 18ms
08-19 09:00:47.729: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 7K, 43% free 3748K/6471K, external 484K/996K, paused 17ms
08-19 09:00:56.817: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1080K, 58% free 2741K/6471K, external 595K/996K, paused 18ms
08-19 09:00:56.848: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3758K/6471K, external 530K/996K, paused 23ms
08-19 09:01:00.701: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2739K/6471K, external 595K/996K, paused 19ms
08-19 09:01:00.731: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3756K/6471K, external 530K/996K, paused 22ms
08-19 09:01:25.916: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2739K/6471K, external 595K/996K, paused 18ms
08-19 09:01:25.946: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3756K/6471K, external 530K/996K, paused 20ms
person Horst Dehmer    schedule 19.08.2011
comment
Спасибо за Ваш ответ. На каком устройстве вы его тестировали? - person Michael; 19.08.2011
comment
Я только что протестировал его на Google Nexus S с Android 2.3.4, и он протекает. Это довольно легко убедиться, если утечка существует. Инициализируйте junk с помощью new byte[10*1204*1024], запустите LeakActivity и несколько раз поверните устройство. Приложение вылетает из-за OOM на Nexus S после второго поворота. - person Michael; 19.08.2011
comment
HTC Sensation с Revolution HD 1.1.8. - person Horst Dehmer; 20.08.2011
comment
Я проверил код с увеличенным размером мусора (5 МБ). Нет проблем. Максимальная куча (в моем случае) сообщается как 32 МБ (ActivityManager.getMemoryClass()). Имейте в виду, что сборщику мусора обычно требуется некоторое время для восстановления памяти. Чего я не знаю, так это того, должен ли блок памяти быть одним фрагментом, то есть нефрагментированным. - person Horst Dehmer; 20.08.2011
comment
Я не знаю, какой размер кучи на HTC Sensation. Попробуйте установить размер junk равным 10 МБ. Когда диалог загрузится, несколько раз поверните устройство. android:configChanges="orientation" нельзя устанавливать для LeakedActivity. Я знаю, что сборщику мусора требуется некоторое время для сбора объектов, но если вы выделяете такой массив в цикле, все будет работать нормально. - person Michael; 20.08.2011
comment
Все еще не проблема. Пожалуйста, проверьте в журнале, что при каждом «перевороте» освобождается примерно 10 МБ. 08-20 15:46:38.944: DEBUG/dalvikvm(13335): GC_FOR_MALLOC освобождено 12093K, 50% свободно 14843K/29575K, внешнее 0K/0K, пауза 80 мс 08-20 15:46:44.169: DEBUG/dalvikvm(13335): GC_FOR_MALLOC освобождено 12100K, 50% свободно 14843K/29575K, внешнее 0K/0K, пауза 68 мс 08-20 15:46:53.498: DEBUG/dalvikvm(13335): GC_FOR_MALLOC освобождено 12099K, 50% свободно 14843K/K0, 2907K/2957K пауза 69 мс - person Horst Dehmer; 20.08.2011
comment
Странный. Я тестировал его на многих устройствах и каждый раз получаю ошибку OOM. К сожалению, у меня нет HTC Sensation, чтобы протестировать его, но спасибо за ваши усилия! - person Michael; 20.08.2011
comment
HTC Desire (2.3.4): 11 МБ тоже нормально, 12 МБ убивает процесс с нехваткой памяти при первом "флипе". Тем не менее, похоже, что утечки памяти нет. Я думаю, это просто слишком большой кусок памяти, который нужно часто выделять. - person Horst Dehmer; 20.08.2011
comment
Не могли бы вы попробовать это с 8 МБ и 3+ флипами? - person Michael; 20.08.2011
comment
Сделано: 8*1024*1024. Ориентацию менял 30 раз без проблем. Память быстро восстанавливается. Даже с 2 или 3 буферами с суммарным размером ‹ 11 Мбайт работает нормально. Вас также может заинтересовать ссылка на "Google I/O 2011: управление памятью для приложений Android" - person Horst Dehmer; 20.08.2011
comment
Большое спасибо. Я не знаю, почему у нас разное поведение с предположительно одним и тем же кодом. Я провел много тестов и абсолютно уверен, что есть утечка памяти. Но я никогда не тестировал его на HTC, и я собираюсь протестировать его в ближайшее время на некоторых устройствах HTC. - person Michael; 20.08.2011
comment
Чтобы убедиться, что мы используем один и тот же код, загляните в мой репозиторий git git://github.com/dehmer/android.git. Проект под названием «com.frequentis.droid.alert» является единственным. - person Horst Dehmer; 20.08.2011
comment
Я тестировал его на всех своих устройствах HTC: Legend (2.2), Wildfire (2.2.1) и Incredible S (2.3.3). И этот баг воспроизводится на каждом устройстве. Не знаю, почему он не воспроизводится на ваших устройствах. Несмотря на это, @Ricky Lee дал несколько советов по поводу этой ошибки и предложил обходной путь. - person Michael; 22.08.2011

Я запустил ваш код, и когда я впервые нажимаю кнопку, он показывает LeakedActivity с диалоговым окном, а onClick удаляет диалоговое окно, но активность остается на переднем плане с черным экраном. При нажатии клавиши «Назад», а затем при повторном запуске действия отображается исключение памяти из-за ошибки:

ERROR/AndroidRuntime(263): java.lang.OutOfMemoryError

Затем я удалил строку private final byte[] junk = new byte[10*1024*1024]; из кода диалога, после чего такой проблемы не существует .... не знаю, почему, если кто-то может выразить это словами, спасибо ему / ей..

I tested the code in emulator android 2.1
person Vineet Shukla    schedule 19.08.2011
comment
Да, я могу сказать вам, почему. Это из-за утечки памяти, о которой я спрашивал. Поле junk было добавлено для более быстрого заполнения кучи. Он должен быть GC'ирован после завершения LeakedActivity, но это не так. И в этом суть. - person Michael; 19.08.2011

Вам необходимо закрыть/отменить диалоговое окно метода onClick, как показано в примере здесь: Диалоговые окна предупреждений в Android

person Brandon Haugen    schedule 17.08.2011
comment
Вы пробовали это? Как видите, этот диалог управляется активностью и автоматически закрывается при уничтожении активности. - person Michael; 17.08.2011