48

Please have a look at the code below:

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;
            }
        }
    }
}

When I use handler like this I get a warning "handler should be static, else it is prone to memory leaks." Can someone tell me what is the best way to do this?

Gary Chen
  • 248
  • 2
  • 14
Rasmus
  • 8,248
  • 12
  • 48
  • 72
  • 2
    I'm not convinced you're using the hander correctly. Have a look at this guide: http://www.vogella.com/articles/AndroidPerformance/article.html. Its not declared as static in the example code there. :/ – Thomas Clayson Jul 01 '12 at 01:18
  • Well even using it like that is giving me the same error. This never used to happen earlier till I upgraded my android sdk last night. Just declaring a handler as a class variable pops up this lint warning now – Rasmus Jul 01 '12 at 01:40
  • Well how about just declaring your handler static? – Zsombor Erdődy-Nagy Jul 03 '12 at 04:58
  • @Zsombor Well I am referring to non static objects inside the handler – Rasmus Jul 03 '12 at 06:45
  • 2
    Check out this [**blog post**](http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html) for a more in depth analysis – Adrian Monk Jan 18 '13 at 18:14

6 Answers6

101

I recently updated something similar in my own code. I just made the anonymous Handler class a protected inner class and the Lint warning went away. See if something like the below code will work for you:

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);
}

You may have to change where I put "theFrag." as I could only guess as to what those referenced.

Rasmus
  • 8,248
  • 12
  • 48
  • 72
Uncle Code Monkey
  • 1,796
  • 1
  • 14
  • 23
  • This seems to be exactly what I want . I have not tested it but there seems to be no reason why it wont work.. Thanks! – Rasmus Jul 05 '12 at 10:18
  • 13
    Shouldn't there be a check after mFrag.get() to check to see if theFrag != null because it may get garbage collected. – Catalin Morosan Sep 07 '12 at 12:29
  • 1
    Depends on your use-case. My personal code made the inner static class definitions private in most cases because the anonymous class definition was not supposed to be used elsewhere (even by descendants). When restricted to just the class it is defined in, whenever the parent Fragment gets destroyed, our inner class reference should be destroyed before Fragment becomes NULL. We use WeakReference so our inner class does not interfere with when Fragment gets garbage collected. If you make your inner class public, or are unsure, please do check to see if theFrag!=null before use. – Uncle Code Monkey Sep 08 '12 at 15:18
  • 1
    This answer absolutely saved me! Thank you for posting this! – ossys Mar 17 '13 at 01:27
  • Is there an advantage to this approach as opposed to simply making the Handler static? – mgibson Aug 06 '13 at 08:48
  • @mgibson yes -- this method exposes the functions and visible members of the parent class, which can then be modified in handleMessage() without also being declared static. Using WeakReference allows the system to work around the possibility of Handler leaks while still avoiding the increased memory footprint that making potentially quite a few fields static would impose – CCJ Oct 23 '13 at 21:30
  • But the message holds on the Handler it's beeing sent to. This way the fragment may be destroyed, but handler would still exists waiting for final messages, but the WeakReference would already be null. I think we should check the result of WeakRefernce.get() is not null. – Kurovsky Oct 09 '14 at 12:19
11

Here's a somewhat useful little class I made that you can use. Sadly it's still quite verbose because you can't have anonymous static inner classes.

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);
}
Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • If I do not need to override handleMessage, just use a handler to post Runnable, can I just use new Handler() – virsir Mar 26 '13 at 08:53
  • 5
    if (mReference.get() == null) return; handleMessage(mReference.get(), msg); is a bad approach because between the null check and handleMessage the reference can still be gc'ed causing null pointer exception, therefore it is a better approach to store the returned value first in T reference = mReference.get() ; then check for null and pass the local variable to the handleMessage method. – Kerem Kusmezer Dec 09 '14 at 09:31
5

Per the ADT 20 Changes, it looks like you should make it static.

New Lint Checks:

Check to make sure that Fragment classes are instantiatable. If you accidentally make a fragment innerclass non-static, or forget to have a default constructor, you can hit runtime errors when the system attempts to reinstantiate your fragment after a configuration change.

Look for handler leaks: This check makes sure that a handler inner class does not hold an implicit reference to its outer class.

Geobits
  • 22,218
  • 6
  • 59
  • 103
  • so how do you think I should handle the above.. Do you need to see more code.. If so please let me know – Rasmus Jul 03 '12 at 06:46
  • If you can't make the Handler static, the best thing may be to move it to the parent Activity and pass a reference to the the Fragment. – Geobits Jul 03 '12 at 15:48
  • Thanks for your replies Geobits...but moving it to the parent Activity still wont make the warning go.Though in my case the inner class wont live longer than the outer one but getting rid of the warning was something I was more concerned about. – Rasmus Jul 05 '12 at 10:21
4

If you read docs about AccountManager or PendingIntent, you will see that some methods take Handler as one of arguments.

For example:

  • onFinished - The object to call back on when the send has completed, or null for no callback.
  • handler - Handler identifying the thread on which the callback should happen. If null, the callback will happen from the thread pool of the process.

Imagine the situation. Some Activity calls PendingIntent.send(...) and put the non-static inner subclass of Handler. And then activity is destroyed. But inner class lives.

Inner class still holds a link to destroyed activity, it cannot be garbage-collected.

If you're not planning to send your handler to such methods, you have nothing to worry about.

QuickNick
  • 1,921
  • 2
  • 15
  • 30
  • 2
    Thanks QuickNick ... Well I do agree with you when you say "If you're not planning to send your handler to such methods, you have nothing to worry about." but I need to get rid of that warning – Rasmus Jul 05 '12 at 10:22
0

I run into the same issue and I find that it is one of this topics with many questions and few answeres. My solution is simple and I hope it can help someone:

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

We can create a static Handler subclass that simply runs a Runnable. The actual handler instance will know what to do through the runnable that will have access to instance variables.

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

The warning is gone while the funcionality is the same.

blurkidi
  • 165
  • 1
  • 3
  • 1
    Thanks for replying Urkidi but for some reason I find this to be more of a hack. I am no expert by any way and might be very wrong – Rasmus Jul 05 '12 at 14:54
  • I like this way though I would like someone with some more knowledge let me know if this is just a hack around. – SatanEnglish Nov 07 '13 at 02:32
  • This hides the lint warning but doesn't resolve the underlying issue. The runnable will have a reference to 'this', the and since the RunnableHandler keeps a reference to the runnable you have the same error just with a more convoluted chain. – Sogger Jan 07 '15 at 17:19
0

A simple solution for this case might be:

Handler handler=new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message message) {
        //do your stuff here
        return false;
    } });
StartCoding
  • 394
  • 5
  • 16