Обработчики и утечки памяти в Android

Пожалуйста, ознакомьтесь с приведенным ниже кодом:

public class MyGridFragment extends Fragment{

     Handler myhandler = new Handler() {
    @Override
    public void handleMessage(Message message) {
        switch (message.what) {
        case 2: {

            ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
            urls.addAll(theurls);
            theimageAdapter.notifyDataSetChanged();
            dismissBusyDialog();
            break;
        }}}};
         }

Когда я использую такой обработчик, я получаю предупреждение: "Обработчик должен быть статичным, иначе он подвержен утечке памяти". Может ли кто-нибудь сказать мне, что это лучший способ сделать это?

Ответ 1

Недавно я обновил что-то подобное в своем собственном коде. Я просто сделал анонимный класс Handler защищенным внутренним классом, и предупреждение Lint исчезло. Посмотрите, будет ли что-то вроде приведенного ниже кода работать для вас:

public class MyGridFragment extends Fragment{

    static class MyInnerHandler extends Handler{
        WeakReference<MyGridFragment> mFrag;

        MyInnerHandler(MyGridFragment aFragment) {
            mFrag = new WeakReference<MyGridFragment>(aFragment);
        }

        @Override
        public void handleMessage(Message message) {
            MyGridFragment theFrag = mFrag.get();
            switch (message.what) {
            case 2:
                ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
                theFrag.urls.addAll(theurls);
                theFrag.theimageAdapter.notifyDataSetChanged();
                theFrag.dismissBusyDialog();
                break;
            }//end switch
        }
    }
    MyInnerHandler myHandler = new MyInnerHandler(this);
}

Возможно, вам придется поменять место, где я разместил "TheFrag". поскольку я мог только догадываться о том, на что ссылаются.

Ответ 2

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

import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Message;

/** A handler which keeps a weak reference to a fragment. According to
 * Android lint, references to Handlers can be kept around for a long
 * time - longer than Fragments for example. So we should use handlers
 * that don't have strong references to the things they are handling for.
 * 
 * You can use this class to more or less forget about that requirement.
 * Unfortunately you can have anonymous static inner classes, so it is a
 * little more verbose.
 * 
 * Example use:
 * 
 *  private static class MsgHandler extends WeakReferenceHandler<MyFragment>
 *  {
 *      public MsgHandler(MyFragment fragment) { super(fragment); }
 * 
 *      @Override
 *      public void handleMessage(MyFragment fragment, Message msg)
 *      {
 *          fragment.doStuff(msg.arg1);
 *      }
 *  }
 * 
 *  // ...
 *  MsgHandler handler = new MsgHandler(this);
 */
public abstract class WeakReferenceHandler<T> extends Handler
{
    private WeakReference<T> mReference;

    public WeakReferenceHandler(T reference)
    {
        mReference = new WeakReference<T>(reference);
    }

    @Override
    public void handleMessage(Message msg)
    {
        if (mReference.get() == null)
            return;
        handleMessage(mReference.get(), msg);
    }

    protected abstract void handleMessage(T reference, Message msg);
}

Ответ 3

В ADT 20 Changes, похоже, что вы должны сделать его статическим.

Новые проверки Lint:

Убедитесь, что классы Fragment являются инстантируемыми. Если вы случайно сделаете фрагмент innerclass нестатический, или забудьте создать конструктор по умолчанию, вы можете нажать runtime ошибки, когда система пытается восстановить ваш фрагмент после изменения конфигурации.

Посмотрите на утечки обработчиков: эта проверка гарантирует, что внутренний класс обработчика не содержит неявная ссылка на его внешний класс.

Ответ 4

Если вы читаете документы об AccountManager или PendingIntent, вы увидите, что некоторые методы принимают Handler как один из аргументов.

Например:

  • onFinished - объект для возврата к завершению отправки, или null для отсутствия обратного вызова.
  • обработчик - Обработчик, определяющий поток, по которому должен произойти обратный вызов. Если значение null, обратный вызов произойдет из пула потоков процесса.

Представьте себе ситуацию. Некоторая активность вызывает PendingIntent.send(...) и помещает нестатический внутренний подкласс обработчика. И тогда активность уничтожается. Но внутренний класс живет.

Внутренний класс по-прежнему содержит ссылку на разрушенную деятельность, ее нельзя собирать с помощью мусора.

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

Ответ 5

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

/* BEFORE */
private Handler mHandler= new Handler() {
        @Override public void handleMessage(Message msg) {
        this.doSomething();
    };
};

Мы можем создать статический подкласс класса Handler, который просто запускает Runnable. Фактический экземпляр обработчика будет знать, что делать через runnable, который будет иметь доступ к переменным экземпляра.

/* AFTER */
static class RunnableHandler extends Handler {
    private Runnable mRunnable;
    public RunnableHandler(Runnable runnable) { 
        mRunnable = runnable;
    }
    @Override public void handleMessage(Message msg) {
        mRunnable.run();
    };
}
private RunnableHandler mHandler = new RunnableHandler(new Runnable() {
    @Override public void run() {
        this.doSomething();
    } });

Предупреждение исчезло, пока функциональность не изменилась.