2012-07-01 6 views
42

Пожалуйста, посмотрите на код ниже:Обработчики и утечки памяти в 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; 
     }}}}; 
     } 

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

+2

Я не уверен, что вы правильно пользуетесь. Посмотрите на это руководство: http://www.vogella.com/articles/AndroidPerformance/article.html. Его не объявляется как статический в примере кода. :/ –

+0

Ну, даже используя его, это дает мне ту же ошибку. Это никогда не случалось раньше, пока я не обновил свой Android-sdk прошлой ночью. Просто объявление обработчика как переменной класса всплывает это предупреждение lint now – Rasmus

+0

Ну как насчет простого объявления вашего статичного обработчика? –

ответ

96

Недавно я обновил что-то подобное в своем собственном коде. Я просто сделал анонимный класс 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». как я мог только догадываться о том, на что ссылаются.

+0

Это похоже на то, что я хочу. Я не тестировал его, но, похоже, нет причин, почему это не сработает. Спасибо! – Rasmus

+12

Не следует ли проверять после mFrag.get(), чтобы проверить, не является ли theFrag! = Null, поскольку он может получить сбор мусора. –

+1

Зависит от вашего прецедента. Мой личный код в большинстве случаев определял внутренние статические определения классов, поскольку определение анонимного класса не должно использоваться в других местах (даже потомками). Если ограничивается только классом, в котором он определен, всякий раз, когда поврежден родительский фрагмент, наша внутренняя ссылка класса должна быть уничтожена до того, как фрагмент станет NULL. Мы используем WeakReference, поэтому наш внутренний класс не мешает, когда Fragment получает сбор мусора. Если вы сделаете свой внутренний класс общедоступным или не уверены, пожалуйста, проверьте, чтобы перед тем, как использовать Frag! = Null. –

5

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

Новых Lint Проверка:

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

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

+0

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

+0

Если вы не можете сделать Handler статическим, лучшим вещь может состоять в том, чтобы переместить его в родительскую активность и передать ссылку на фрагмент. – Geobits

+0

Спасибо за ваши ответы Geobits ... но переместив его в родительскую активность, все равно не сделайте предупреждение. Хотя в моем случае внутренний класс не будет жить дольше, чем внешний, но избавление от предупреждения было чем-то, что меня больше беспокоило , – Rasmus

3

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

For example:

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

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

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

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

+2

Спасибо QuickNick ... Ну, я согласен с вами, когда вы говорите: «Если вы не планируете отправлять свой обработчик таким методам, вам не о чем беспокоиться». но мне нужно избавиться от этого предупреждения. – Rasmus

0

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

/* 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(); 
    } }); 

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

+1

Спасибо, что ответили Urkidi, но почему-то я считаю, что это скорее хак. Я не эксперт никоим образом и могу быть очень неправильным – Rasmus

+0

Мне нравится этот путь, хотя я бы хотел, чтобы кто-то с некоторыми знаниями дал мне знать, если это всего лишь взломать. – SatanEnglish

+0

Это скрывает предупреждение ворса, но не устраняет основную проблему. В runnable будет ссылка на «this», а так как RunnableHandler сохраняет ссылку на runnable, у вас есть такая же ошибка только с более запутанной цепочкой. – Sogger

11

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

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's 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); 
} 
+0

Если мне не нужно переопределять handleMessage, просто используйте обработчик для отправки Runnable, могу ли я просто использовать новый обработчик() – virsir

+2

if (mReference.get() == null) return; handleMessage (mReference.get(), msg); является плохим подходом, поскольку между нулевой проверкой и handleMessage эта ссылка все еще может быть вызвана вызовом исключения нулевого указателя, поэтому это лучший подход для сохранения возвращаемого значения сначала в T reference = mReference.get(); затем проверьте значение null и передайте локальную переменную методу handleMessage. –

+0

Хорошая точка Kerem. Не стесняйтесь редактировать код, если хотите! – Timmmm

Смежные вопросы