Экземпляр активности все еще существует даже после вызова onDestroy()
Я передаю обработчик, созданный в потоке mainUI из Activity
, и передается потоку, который выполняет некоторую сетевую операцию, и когда я получаю результат, я возвращаю результат к активности с помощью обработчика.
Этот подход имел проблемы с утечками памяти, когда я просматривал эти ссылки: Внутренний утечка памяти классаHandler
Разработчики Android
Итак, я реализовал WeakReference
и сохранил экземпляр активности с помощью WeakReference
. Но я все еще вижу экземпляр Activity
, живой даже после уничтожения активности.
Я создал внутреннюю активность Handler
и передал экземпляр активности как слабое отношение к обработчику.
К моменту, когда мой Handler
отвечает сообщением, переданным ему через 10 секунд, Activity
уничтожается. Но у слабой ссылки все еще есть экземпляр Activity
, и я вижу Toast
, после того как Activity
будет уничтожен.
Есть ли там, где мое понимание не так?
Может кто-нибудь объяснить, как обрабатывать сообщения, доставленные обработчику, но пользовательский интерфейс не вокруг?
import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Message;
public abstract class SingleParamHandler <T> extends Handler
{
private WeakReference<T> mActivityReference;
public SingleParamHandler(T activity) {
mActivityReference = new WeakReference<T>(activity);
}
@Override
public void handleMessage(Message msg) {
if (mActivityReference.get() == null) {
return;
}
handleMessage(mActivityReference.get(), msg);
}
protected abstract void handleMessage(T activity, Message msg);
}
import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.widget.Toast;
public class MainActivity extends Activity {
MyHandler<MainActivity> handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main1);
handler = new MyHandler<MainActivity>(this);
new Thread(new MyRunnable(handler)).start();
}
public void onDestroy() {
super.onDestroy();
System.out.println("######## Activity onDestroy() ###### ");
}
private class MyRunnable implements Runnable {
private Handler mHandler;
public MyRunnable(Handler handler) {
mHandler = handler;
}
public void run() {
try {
Thread.sleep(10000);
mHandler.sendMessage(Message.obtain(handler, 1));
} catch ( Exception e) {
e.printStackTrace();
}
}
}
private static class MyHandler<T> extends SingleParamHandler<T> {
public MyHandler(T activity) {
super(activity);
}
@Override
public void handleMessage(T act, Message msg) {
if(msg.what == 1) {
Toast.makeText((MainActivity)act, "Called after activity destroyed", Toast.LENGTH_LONG).show();;
}
}
}
}
Основываясь на полученном ответе, я обновляю ответ здесь. Вы можете сделать это так, как вам понравилось. Но это один из способов.
Добавлена следующая функция в SingleParamHandler
public void clear() {
mActivityReference.clear();
}
А в действии onDestroy()
public void onDestroy() {
super.onDestroy();
System.out.println("######## Activity onDestroy() ###### ");
handler.clear();
}
Ответы
Ответ 1
Здесь вам не нужен WeakReference
. Handler
может содержать только ссылку на Activity
. В действии onDestroy()
просто вызовите метод на MyHandler
, который устанавливает ссылку на Activity
на null
. Проверьте null
на handleMessage()
.
Другим выбором будет следующее: в действии onDestroy()
вызвать метод, который прерывает спящий поток, чтобы он отключился перед отправкой сообщения.
Ответ 2
Там нет гарантии, что Android действительно удалит объект из памяти, если этого не потребуется. Другими словами, объекты Activity могут оставаться в памяти даже после вызова onDestroy()
(если имеется достаточно памяти). С другой стороны, нет гарантии, что onDestroy()
будет вызываться, если памяти недостаточно; Скорее наоборот, Android может убить весь ваш процесс после вызова onPause()
в текущем действии (в зависимости от версии Android).
Я думаю, что есть лучший путь для вашей цели. То, что вы, возможно, захотите сделать, - это подключить, отсоединить и, возможно, повторно подключить (например, к изменениям конфигурации) к вашей службе. Не надейтесь, что сборщик мусора сделает вам работу. Скорее сделайте это явно.
Подкласс Activity
и переопределить методы жизненного цикла, а также startActivity()
и startActivityForResult()
, чтобы ваша служба узнала, кто отвечает за это прямо сейчас. Разумеется, это только подход с наилучшими усилиями, поскольку некоторые обратные вызовы не гарантируются, но это имеет значение только в определенных ситуациях, которые не являются опасными. Например, ваша деятельность не будет отсоединяться от вашего Сервиса в onPause()
, но впоследствии она может быть убита. Но ваша служба работает в том же процессе, поэтому ее одновременно убивают. Или он работает в другом процессе, но тогда Android заметит сломанное соединение и может или не может убить службу; если нет, то все, что вам нужно сделать, это реализовать его в надежной форме, чтобы иметь возможность справиться с потерей соединения.
Обновление
После прочтения вашего комментария: вы правы, я не обращался к этому конкретно.
Я выясняю, как избежать отправки сообщений обработчику, который создается в уничтоженной деятельности
Учитывая ваш код выше и предполагая, что вы действительно хотите отображать Toast
с активностью, если она существует, этот подход должен помочь.
- Если ваш
Thread
должен обслуживать более одного действия, расширьте его так, чтобы Activities мог регистрироваться в Thread после его создания. Если ваш Thread
просто служит одному Activity
, передайте ссылку Activity
вместе с ссылкой Handler
на конструкцию Thread
(Runnable
).
- Прежде чем ваш
Thread
отправит сообщение через Handler
, отметьте activity.isDestroyed()
. Если действие не уничтожено, отправьте сообщение. Если действие уничтожено, не отправляйте сообщение.
- В зависимости от того, должен ли ваш поток иметь сервер более одного действия, либо выйдите из него
Runnable
run()
или установите его Activity
ссылку на null
, если обнаружит, что Activity
был уничтожен.
Это должно исправить ваш вышеуказанный код. Однако, если ваш сценарий растет, другие подходы могут быть более подходящими.