Экземпляр активности все еще существует даже после вызова 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 был уничтожен.

Это должно исправить ваш вышеуказанный код. Однако, если ваш сценарий растет, другие подходы могут быть более подходящими.