В обычном программировании Android Handler часто используется при выполнении асинхронных операций и обработке возвращаемых результатов. Обычно наш код реализуется так.
Скопируйте код кода следующим образом:
публичный класс SampleActivity расширяет Activity {
частный окончательный обработчик mLeakyHandler = новый Handler() {
@Override
public void handleMessage (Сообщение сообщения) {
// ...
}
}
}
Однако на самом деле приведенный выше код может вызвать утечку памяти. Когда вы используете инструмент Android lint, вы получите такое предупреждение.
Скопируйте код кода следующим образом:
В Android классы обработчиков должны быть статическими, иначе могут возникнуть утечки. Сообщения, помещенные в очередь MessageQueue потока приложения, также сохраняют свой целевой обработчик. Если обработчик является внутренним классом, его внешний класс также будет сохранен. Во избежание утечки внешнего класса. объявите Handler как статический вложенный класс с WeakReference на его внешний класс
Увидев это, вы все еще можете быть в замешательстве. Где в коде может возникнуть утечка памяти и как она может вызвать утечку памяти? Тогда давайте медленно проанализируем это.
1. При запуске приложения Android автоматически создается экземпляр Looper для использования основным потоком приложения. Основная задача Looper — обрабатывать объекты сообщений в очереди сообщений один за другим. В Android все события платформы Android (такие как вызовы методов жизненного цикла активности, нажатия кнопок и т. д.) помещаются в сообщения, а затем добавляются в очередь сообщений для обработки Looper, и Looper отвечает за их обработку. по одному. Жизненный цикл Looper в основном потоке равен текущему приложению.
2. Когда обработчик инициализируется в основном потоке и мы отправляем сообщение, предназначенное для этого обработчика, в очередь сообщений, обрабатываемую Looper, фактически отправленное сообщение уже содержит ссылку на экземпляр обработчика. Только в этом случае обработка Looper выполняется только в этом случае. сообщение получено, можно ли вызвать Handler#handleMessage(Message) для завершения правильной обработки сообщения.
3. В Java нестатические внутренние классы и анонимные внутренние классы неявно содержат ссылки на свои внешние классы. Статические внутренние классы не содержат ссылок на внешние классы. Дополнительную информацию об этом см. в разделе Чат Java: «Недопустимый» частный модификатор.
Это правда, что в приведенном выше примере кода немного сложно обнаружить утечку памяти, тогда следующий пример очень очевиден.
Скопируйте код кода следующим образом:
публичный класс SampleActivity расширяет Activity {
частный окончательный обработчик mLeakyHandler = новый Handler() {
@Override
public void handleMessage (Сообщение сообщения) {
// ...
}
}
@Override
protected void onCreate (Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
// Опубликовать сообщение и задержать его выполнение на 10 минут.
mLeakyHandler.postDelayed(новый Runnable() {
@Override
public void run() { /* ... */ }
}, 1000*60*10);
// Вернемся к предыдущему действию.
заканчивать();
}
}
Анализируя приведенный выше код, когда мы выполняем метод завершения действия, задержанное сообщение будет существовать в очереди сообщений основного потока в течение 10 минут, прежде чем будет обработано, и это сообщение содержит ссылку на обработчик, а обработчик является анонимным. Экземпляр внутренний класс содержит ссылку на внешний SampleActivity, поэтому невозможно перезапустить SampleActivity. В результате многие ресурсы, хранящиеся в SampleActivity, не могут быть переработаны. Это то, что мы часто называем утечкой памяти.
Обратите внимание, что новый Runnable, описанный выше, также реализован анонимным внутренним классом. Он также содержит ссылку на SampleActivity и предотвращает повторное использование SampleActivity.
Чтобы решить эту проблему, идея состоит в том, чтобы не использовать нестатические внутренние классы. При наследовании Handler либо поместите его в отдельный файл класса, либо используйте статический внутренний класс. Поскольку статический внутренний класс не содержит ссылок на внешний класс, это не приведет к утечкам памяти экземпляра внешнего класса. Когда вам нужно вызвать внешнее действие в статическом внутреннем классе, мы можем использовать слабые ссылки для его обработки. Кроме того, Runnable также необходимо установить как статический атрибут члена. Примечание. Статический анонимный экземпляр внутреннего класса не содержит ссылки на внешний класс. Код, который не вызовет утечки памяти после модификации, выглядит следующим образом:
Скопируйте код кода следующим образом:
публичный класс SampleActivity расширяет Activity {
/**
* Экземпляры статических внутренних классов не содержат неявного
* ссылка на их внешний класс.
*/
частный статический класс MyHandler расширяет Handler {
частный окончательный WeakReference<SampleActivity> mActivity;
public MyHandler (активность SampleActivity) {
mActivity = новый WeakReference<SampleActivity>(активность);
}
@Override
public void handleMessage (Сообщение сообщения) {
Действие SampleActivity = mActivity.get();
если (активность != ноль) {
// ...
}
}
}
частный окончательный MyHandler mHandler = новый MyHandler(this);
/**
* Экземпляры анонимных классов не содержат неявного
* ссылка на их внешний класс, когда они «статические».
*/
частный статический окончательный Runnable sRunnable = новый Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate (Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
// Опубликовать сообщение и задержать его выполнение на 10 минут.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Вернемся к предыдущему действию.
заканчивать();
}
}
Фактически, многие утечки памяти в Android вызваны использованием нестатических внутренних классов в Activity, как упоминалось в этой статье, поэтому мы должны уделять особое внимание при использовании нестатических внутренних классов, если их экземпляр If жизненный цикл. удерживаемый объект больше, чем объект его внешнего класса, это может привести к утечкам памяти. Лично я склонен использовать статический класс статьи и слабые ссылочные методы для решения этой проблемы.