Хендлеры и внутренние классы. Утечки памяти

Home » android » Хендлеры и внутренние классы. Утечки памяти

оригинал
здесь ссылка=связь=reference

Возьмем следующий код:

Хотя это и может показаться неочевидным, такой код вызывает существенную утечку памяти. Android Lint выдает следующее предупреждение:

In Android, Handler classes should be static or leaks might occur.

Но где именно происходит утечка? Давайте попробуем определить проблему исходя из имеющихся фактов:

  1. Когда впервые стартует андроид-приложение, фреймворк создает объект Looper, для главного потока приложения. Looper имплементирует простую очередь сообщений, обрабатывает объекты Message в цикле(loop) один вслед за другим в процессе очереди. Все основные события фреймворка приложения(такие как вызовы методов жизненного цикла Activity, события слушателей(нажатия кнопок) и др.) содержатся внутри объектов Message. Которые могут быть добавлены в очередь Looper, и которые могут быть обработаны только в порядке очереди, один за другим. Looper основного потока приложения существует в рамках жизненного цикла приложения.
  2. Когда мы создаем экземпляр Handler-а в главном потоке, он ассоциируется с очередью сообщений Looper-а. Сообщения поставленные в очередь сообщений содержат ссылку на Handler, так что фреймворк может вызвать Handler#handleMessage(Message) когда Looper обрабатывает сообщение.
  3. В Java нестатические внутренние классы(вложенные классы) и анонимные классы содержат имплицитную ссылку на их внешние классы. Статические внутренние классы, с другой стороны, такой ссылки не содержат

Итак, где же именно происходит утечка? Заметить это трудно, так как это неявно, но, предположим у нас есть следующий код:

Когда активность завершается, отложенное сообщение будет продолжать жить в основном потоке 10 минут, перед тем как будет обработано. Это сообщение содержит ссылку на Handler активности, и Handler содержит внутреннюю ссылку на свой внешний класс(в нашем примере mLeakyHandler содержит ссылку на SampleActivity). Эта ссылка ссылка будет сохраняться до тех пор, пока сообщение не будет обработано, таким образом контекст активности не сможет быть собран сборщиком мусора(гарбедж коллектором), и ресурсы для дальнейшего выполнения приложения не будут освобождены. То же самое верно и для анонимных объектов Runnable. Не статические экземпляры анонимных классов содержат внутреннюю(имплицитную) ссылку на свои внешние классы, так что утечки памяти будет точно такой же.

Чтобы избавится от проблемы утечки памяти, следует использовать подкласс класса Handler в новом файле, или использовать статические внутренние классы. Статические внутренние классы не содержат имплицитные ссылки на их внешние классы, следовательно активность не создает проблемы утечки памяти. Если вам необходимо вызвать методы внешней активности внутри Handler, можно обеспечить слабую связь(WeakReference) с активностью, и контекст потерян не будет. Чтобы избежать утечки памяти, которая появляется когда мы создаем экземпляр анонимного Runnable класса, мы создаем переменную, статический член класса(так как статические экземпляры анонимных классов не содержат имплицитных ссылок(связей) на свои внешние классы).

Разница между мтатическими и динамическими внутренними классами неявна, но ее необходимо понимать каждому разработчику. На чем же подвести черту? – Избегайте использования вложенных(динамических внутренних классов в активности, если экземпляры внутренних классов могут “пережить” время жизни активности. Вместо этого, следует предпочесть употребление статичских внутренних классов и поддерживать слабую связь с активностью внутри.

что такое имплицитные связи(ссылки)?(внутренних классов на внешние)
Подробно о мягких ссылках и слабых ссылках

http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
http://stackoverflow.com/questions/1520887/how-to-pause-sleep-thread-or-process-in-android

LEAVE A COMMENT