это - статические классы java



Когда именно это безопасно для утечки для использования(анонимных) внутренних классов? (1)

То, что вы задаете, - довольно сложный вопрос. Хотя вы можете подумать, что это всего лишь один вопрос, вы на самом деле задаете сразу несколько вопросов. Я сделаю все возможное, зная, что я должен его покрыть, и, надеюсь, некоторые другие присоединятся, чтобы охватить то, что я могу пропустить.

Вложенные классы: введение

Поскольку я не уверен, насколько вам комфортно с ООП в Java, это приведет к паре основ. Вложенный класс - это когда определение класса содержится в другом классе. Существуют, в основном, два типа: статические вложенные классы и внутренние классы. Реальная разница между ними:

  • Статические вложенные классы:
    • Рассматриваются «высшие уровни».
    • Не требует создания экземпляра содержащего класса.
    • Нельзя ссылаться на содержащихся членов класса без явной ссылки.
    • Имейте свою собственную жизнь.
  • Внутренние вложенные классы:
    • Всегда требуется экземпляр содержащего класса для построения.
    • Автоматически иметь неявную ссылку на содержащий экземпляр.
    • Может получить доступ к членам класса контейнера без ссылки.
    • Предполагается, что срок службы не должен превышать срок службы контейнера.

Сбор мусора и внутренние классы

Сбор мусора автоматически, но пытается удалить объекты на основе того, думает ли он, что они используются. Сборщик мусора довольно умный, но не безупречный. Он может только определить, используется ли что-то, независимо от того, активна ли ссылка на объект.

Настоящая проблема заключается в том, что внутренний класс был сохранен дольше, чем его контейнер. Это связано с неявной ссылкой на содержащий класс. Единственный способ, которым это может произойти, - это если объект за пределами содержащего класса сохраняет ссылку на внутренний объект без учета содержащего объекта.

Это может привести к ситуации, когда внутренний объект жив (через ссылку), но ссылки на содержащий объект уже удалены из всех других объектов. Поэтому внутренний объект сохраняет живой объект, потому что он всегда будет ссылаться на него. Проблема заключается в том, что, если она не запрограммирована, нет способа вернуться к содержащему объекту, чтобы проверить, даже ли он жив.

Важнейшим аспектом этой реализации является то, что не имеет значения, находится ли она в Activity или является ли она пригодной. Вы всегда должны быть методичными при использовании внутренних классов и следить за тем, чтобы они никогда не переживали объекты контейнера. К счастью, если это не основной объект вашего кода, утечки могут быть небольшими по сравнению. К сожалению, это самые трудные утечки, потому что они, вероятно, останутся незамеченными, пока многие из них не просочились.

Решения: Внутренние классы

  • Получите временные ссылки из содержащего объекта.
  • Разрешить содержащему объекту быть единственным, кто сохранит долгоживущие ссылки на внутренние объекты.
  • Используйте установленные шаблоны, например Factory.
  • Если внутренний класс не требует доступа к содержащимся членам класса, подумайте о том, чтобы превратить его в статический класс.
  • Используйте с осторожностью, независимо от того, находится ли она в действии или нет.

Виды деятельности и взгляды: Введение

Мероприятия содержат много информации для запуска и отображения. Действия определяются характеристикой, что у них должен быть вид. У них также есть определенные автоматические обработчики. Указываете ли вы это или нет, Activity имеет неявную ссылку на содержащуюся в нем View.

Чтобы созданный вид был создан, он должен знать, где его создать, и есть ли у него дети, чтобы он мог отображаться. Это означает, что каждый вид имеет ссылку на Activity (через getContext() ). Более того, каждый просмотр сохраняет ссылки на своих детей (т.е. getChildAt() ). Наконец, каждый вид сохраняет ссылку на визуализированный растровый файл, который представляет его отображение.

Всякий раз, когда у вас есть ссылка на Activity (или Activity Context), это означает, что вы можете следовать цепочке ENTIRE по иерархии макета. Вот почему утечка памяти в отношении «Деятельности» или «Виды» - вот такая огромная сделка. Это может быть тонна памяти, просочившаяся сразу.

Виды деятельности, виды и внутренние классы

Учитывая информацию выше о внутренних классах, это наиболее распространенные утечки памяти, но также наиболее часто избегаемые. Хотя желательно, чтобы внутренний класс имел прямой доступ к членам класса «Деятельность», многие из них готовы сделать их статическими, чтобы избежать потенциальных проблем. Проблема с действиями и представлениями идет гораздо глубже, чем это.

Протекаемые действия, представления и контексты деятельности

Все сводится к контексту и жизненному циклу. Существуют определенные события (например, ориентация), которые будут убивать контекст активности. Поскольку для многих классов и методов требуется контекст, разработчики иногда пытаются сохранить некоторый код, захватив ссылку на контекст и удерживая его. Так получилось, что многие объекты, которые мы должны создать для запуска нашей деятельности, должны существовать вне цикла Life Activity, чтобы позволить Activity делать то, что нужно. Если у какого-либо из ваших объектов есть ссылка на Activity, его Контекст или любой из его представлений, когда он был уничтожен, вы только что просочились из этой Activity и всего дерева View.

Решения: действия и виды

  • Избегайте, во что бы то ни стало, статическую ссылку на представление или активность.
  • Все ссылки на контексты активности должны быть короткими (продолжительность функции)
  • Если вам нужен долговременный контекст, используйте Контекст приложения ( getBaseContext() или getApplicationContext() ). Они не поддерживают ссылки неявно.
  • В качестве альтернативы вы можете ограничить уничтожение Activity путем переопределения изменений конфигурации. Однако это не останавливает другие потенциальные события от уничтожения Activity. Хотя вы можете это сделать, вы все равно можете обратиться к вышеуказанным практикам.

Runnables: Введение

Бегны на самом деле не так уж плохи. Я имею в виду, они могут быть, но на самом деле мы уже попали в большинство опасных зон. Runnable - это асинхронная операция, выполняющая задачу независимо от потока, на котором она была создана. Большинство runnables создаются из потока пользовательского интерфейса. По сути, использование Runnable создает другой поток, чуть более управляемый. Если вы классифицируете Runnable как стандартный класс и следуете приведенным выше рекомендациям, вы должны столкнуться с несколькими проблемами. Реальность такова, что многие разработчики этого не делают.

Из-за непринужденности, удобочитаемости и логического потока программ многие разработчики используют Анонимные Внутренние Классы для определения своих Runnables, таких как пример, который вы создали выше. Это приводит к примеру, подобному тому, который вы набрали выше. Анонимный внутренний класс - это в основном дискретный внутренний класс. Вам просто не нужно создавать совершенно новое определение и просто переопределять соответствующие методы. Во всех других отношениях это внутренний класс, что означает, что он сохраняет неявную ссылку на свой контейнер.

Runnables and Activities / Views

Ура! Этот раздел может быть коротким! В связи с тем, что Runnables работают за пределами текущего потока, опасность для них связана с длительными асинхронными операциями. Если runnable определен в Activity или View как анонимный внутренний класс или вложенный внутренний класс, существуют некоторые очень серьезные опасности. Это связано с тем, что, как было сказано ранее, он должен знать, кто его контейнер. Введите изменение ориентации (или систему). Теперь просто вернитесь к предыдущим разделам, чтобы понять, что только что произошло. Да, ваш пример довольно опасен.

Решения: Runnables

  • Попробуйте расширить Runnable, если он не нарушает логику вашего кода.
  • Сделайте все возможное, чтобы сделать расширенные Runnables статичными, если они должны быть вложенными классами.
  • Если вы должны использовать Анонимные Runnables, избегайте их создания в любом объекте, который имеет долговременную ссылку на активную или Просмотр, которая используется.
  • Многие Runnables могли так же легко быть AsyncTasks. Рассмотрите возможность использования AsyncTask, поскольку по умолчанию управляются VM.

Отвечая на окончательный вопрос Теперь, чтобы ответить на вопросы, которые не были напрямую решены другими разделами этого сообщения. Вы спросили: «Когда объект внутреннего класса выживет дольше, чем его внешний класс?» Прежде чем мы дойдем до этого, позвольте мне еще раз подчеркнуть: хотя вы правы, чтобы беспокоиться об этом в действиях, это может вызвать утечку в любом месте. Я приведу простой пример (без использования Activity) только для демонстрации.

Ниже приведен общий пример базовой фабрики (отсутствует код).

public class LeakFactory
{//Just so that we have some data to leak
    int myID = 0;
// Necessary because our Leak class is an Inner class
    public Leak createLeak()
    {
        return new Leak();
    }

// Mass Manufactured Leak class
    public class Leak
    {//Again for a little data.
       int size = 1;
    }
}

Это не такой общий пример, но достаточно простой, чтобы продемонстрировать. Ключевым здесь является конструктор ...

public class SwissCheese
{//Can't have swiss cheese without some holes
    public Leak[] myHoles;

    public SwissCheese()
    {//Gotta have a Factory to make my holes
        LeakFactory _holeDriller = new LeakFactory()
    // Now, let's get the holes and store them.
        myHoles = new Leak[1000];

        for (int i = 0; i++; i<1000)
        {//Store them in the class member
            myHoles[i] = _holeDriller.createLeak();
        }

    // Yay! We're done! 

    // Buh-bye LeakFactory. I don't need you anymore...
    }
}

Теперь у нас есть утечки, но нет фабрики. Несмотря на то, что мы выпустили Factory, он останется в памяти, потому что у каждого Leak есть ссылка на него. Даже не имеет значения, что внешний класс не имеет данных. Это происходит гораздо чаще, чем можно было бы подумать. Нам не нужен создатель, просто его творения. Поэтому мы создаем один временно, но используем творения бесконечно.

Представьте, что происходит, когда мы слегка меняем конструктор.

public class SwissCheese
{//Can't have swiss cheese without some holes
    public Leak[] myHoles;

    public SwissCheese()
    {//Now, let's get the holes and store them.
        myHoles = new Leak[1000];

        for (int i = 0; i++; i<1000)
        {//WOW! I don't even have to create a Factory... 
        // This is SOOOO much prettier....
            myHoles[i] = new LeakFactory().createLeak();
        }
    }
}

Теперь, каждая из этих новых LeakFactories только что просочилась. Что вы думаете об этом? Это два очень распространенных примера того, как внутренний класс может пережить внешний класс любого типа. Если бы этот внешний класс был Управлением, представьте, насколько это было бы хуже.

Заключение

Они перечисляют прежде всего известные опасности использования этих объектов ненадлежащим образом. В общем, этот пост должен был охватить большинство ваших вопросов, но я понимаю, что это была должность loooong, поэтому, если вам нужно разъяснение, просто дайте мне знать. Пока вы будете следовать вышеуказанным практикам, вы будете очень мало беспокоиться об утечке.

Я читал некоторые статьи об утечке памяти в Android и смотрел это интересное видео из Google I / O по этому вопросу .

Тем не менее, я не совсем понимаю эту концепцию и особенно когда она безопасна или опасна для внутренних классов пользователя внутри Activity .

Вот что я понял:

Утечка памяти произойдет, если экземпляр внутреннего класса сохранится дольше, чем его внешний класс (Activity). -> В каких ситуациях это может произойти?

В этом примере, я полагаю, нет никакого риска утечки, потому что нет способа анонимного класса, расширяющего OnClickListener , жить дольше, чем активность, не так ли?

    final Dialog dialog = new Dialog(this);
    dialog.setContentView(R.layout.dialog_generic);
    Button okButton = (Button) dialog.findViewById(R.id.dialog_button_ok);
    TextView titleTv = (TextView) dialog.findViewById(R.id.dialog_generic_title);

    // *** Handle button click
    okButton.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            dialog.dismiss();
        }
    });

    titleTv.setText("dialog title");
    dialog.show();

Теперь, этот пример опасен, и почему?

// We are still inside an Activity
_handlerToDelayDroidMove = new Handler();
_handlerToDelayDroidMove.postDelayed(_droidPlayRunnable, 10000);

private Runnable _droidPlayRunnable = new Runnable() { 
    public void run() {
        _someFieldOfTheActivity.performLongCalculation();
    }
};

У меня есть сомнение в том, что понимание этой темы связано с пониманием подробно того, что сохраняется, когда деятельность уничтожается и воссоздается.

Это?

Скажем, я просто изменил ориентацию устройства (что является наиболее распространенной причиной утечек). Когда super.onCreate(savedInstanceState) будет вызываться в моем onCreate() , это восстановит значения полей (как это было до изменения ориентации)? Будет ли это также восстанавливать состояния внутренних классов?

Я понимаю, что мой вопрос не очень точен, но я бы очень признателен за любое объяснение, которое могло бы сделать все более ясным.





inner-classes