314

I'm developing an Android 2.3.3 application with a service. I have this inside that service to communicate with Main activity:

public class UDPListenerService extends Service
{
    private static final String TAG = "UDPListenerService";
    //private ThreadGroup myThreads = new ThreadGroup("UDPListenerServiceWorker");
    private UDPListenerThread myThread;
    /**
     * Handler to communicate from WorkerThread to service.
     */
    private Handler mServiceHandler;

    // Used to receive messages from the Activity
    final Messenger inMessenger = new Messenger(new IncomingHandler());
    // Use to send message to the Activity
    private Messenger outMessenger;

    class IncomingHandler extends Handler
    {
        @Override
        public void handleMessage(Message msg)
        {
        }
    }

    /**
     * Target we publish for clients to send messages to Incoming Handler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());
    [ ... ]
}

And here, final Messenger mMessenger = new Messenger(new IncomingHandler());, I get the following Lint warning:

This Handler class should be static or leaks might occur: IncomingHandler

What does it mean?

Dheeraj Vepakomma
  • 26,870
  • 17
  • 81
  • 104
VansFannel
  • 45,055
  • 107
  • 359
  • 626
  • 27
    Check out this [**blog post**](http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html) for a lot more information on this subject! – Adrian Monk Jan 18 '13 at 18:15
  • 2
    Memory leaks caused by garbage collection... This is enough to demonstrate how Java is inconsistent and badly designed – Gojir4 Jun 06 '18 at 11:30

8 Answers8

399

If IncomingHandler class is not static, it will have a reference to your Service object.

Handler objects for the same thread all share a common Looper object, which they post messages to and read from.

As messages contain target Handler, as long as there are messages with target handler in the message queue, the handler cannot be garbage collected. If handler is not static, your Service or Activity cannot be garbage collected, even after being destroyed.

This may lead to memory leaks, for some time at least - as long as the messages stay int the queue. This is not much of an issue unless you post long delayed messages.

You can make IncomingHandler static and have a WeakReference to your service:

static class IncomingHandler extends Handler {
    private final WeakReference<UDPListenerService> mService; 

    IncomingHandler(UDPListenerService service) {
        mService = new WeakReference<UDPListenerService>(service);
    }
    @Override
    public void handleMessage(Message msg)
    {
         UDPListenerService service = mService.get();
         if (service != null) {
              service.handleMessage(msg);
         }
    }
}

See this post by Romain Guy for further reference

Ahmad Kayyali
  • 8,233
  • 13
  • 49
  • 83
Tomasz Niedabylski
  • 5,768
  • 1
  • 18
  • 20
  • Thanks for your answer. How can I fix this issue? – VansFannel Jul 10 '12 at 07:14
  • Thank you very much for this answer. The lint warning about static handlers always mystified me and after searching around for an hour or so on this topic your answer explained it well and even gave a source of Romain Guy, if I could give it a second upvote I would – snctln Jul 11 '12 at 03:56
  • 3
    Romain shows that the WeakReference to the outer class is all that's needed - a static nested class is not necessary. I think I'll prefer the WeakReference approach because otherwise the whole outer class drastically changes due to all the 'static' variables I'd need. – Someone Somewhere Jul 18 '12 at 19:07
  • 37
    If you want to use a nested class, it has to be static. Otherwise, WeakReference doesn't change anything. Inner (nested but not static) class always holds strong reference to outer class. There is no need for any static variables though. – Tomasz Niedabylski Jul 18 '12 at 21:24
  • OK. Thanks for re-iterating that the Handler class must be static. My Handler calls functions within the outer class depending on the message received, so really my entire service (outer class) will be massively affected if the inner Handler class needs to be static. – Someone Somewhere Jul 18 '12 at 23:41
  • Tomasz, what happens when `mService.get() == null` ? What happens to the message ? Is it actually possible to receive a null from mService.get() ? – Someone Somewhere Jul 23 '12 at 17:47
  • 2
    @SomeoneSomewhere mSerivce is a WeakReference. `get()` will return null when referenced object was gc-ed. In this case, when service is dead. – Tomasz Niedabylski Jul 23 '12 at 23:02
  • @Tomasz Thanks for writing this answer. It was really useful. – wojciii Oct 02 '12 at 10:14
  • @Macarse Dear Macarse. Can u edit u answer and show how u declared UDPListenerService? – user170317 Oct 05 '12 at 07:22
  • Service is same as in the question. Only have to add handleMesage(msg) method in it. – Tomasz Niedabylski Oct 05 '12 at 08:29
  • @Macarse can i avoid to extent Handler class? i was try to subclass and using it by public WeakHandler uaReceiverHandler = new WeakHandler(phonePadForUsing); but hangleMessage was not fired ( – user170317 Oct 05 '12 at 10:22
  • 2
    Note: after making the IncomingHandler static, I was getting the error "The constructor MyActivity.IncomingHandler() is undefined." on the line "final Messenger inMessenger = new Messenger(new IncomingHandler());". The solution is to change that line to "final Messenger inMessenger = new Messenger(new IncomingHandler(this));". – Lance Lefebure Feb 05 '13 at 03:18
  • @Tomasz Niedabylski I get an error that the final field cannot be assigned (mService is declared final, then we try to assign it in the constructor). Also, if the handler is static, then getApplicationContext() does not work so I can't produce a toast. Any thoughts on either of these issues? – David Doria Oct 01 '13 at 15:16
  • Make sure mService is only declared, not assigned at declaration (final `mService = null;` is a mistake). As for the toast, put the `code in service.handleMessage()` – Tomasz Niedabylski Oct 01 '13 at 16:54
  • 4
    @Someone Somewhere Yeah, Romain's post is wrong in that he missed declaring the inner class static which misses the whole point. Unless he has some ultra cool compiler that automatically converts inner classes to static classes when they don't use class variables. – Sogger Jan 07 '15 at 18:22
  • Android developers use this approach so it can't be that bad, right? http://developer.android.com/reference/android/app/Service.html#LocalServiceSample – JohnyTex Jan 13 '16 at 10:32
74

As others have mentioned the Lint warning is because of the potential memory leak. You can avoid the Lint warning by passing a Handler.Callback when constructing Handler (i.e. you don't subclass Handler and there is no Handler non-static inner class):

Handler mIncomingHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // todo
        return true;
    }
});

As I understand it, this will not avoid the potential memory leak. Message objects hold a reference to the mIncomingHandler object which holds a reference the Handler.Callback object which holds a reference to the Service object. As long as there are messages in the Looper message queue, the Service will not be GC. However, it won't be a serious issue unless you have long delay messages in the message queue.

Aaron
  • 168
  • 1
  • 10
Michael
  • 1,133
  • 10
  • 11
  • 13
    @Braj I don't think avoiding the lint warning but still keeping the error is a good solution at all. Unless, as the lint warning states if the handler is not put on your main looper (and you can ensure all the messages pending on it are destroyed when the class is destroyed) then leaking the reference is mitigated. – Sogger Jan 07 '15 at 18:29
33

Here is a generic example of using a weak reference and static handler class to resolve the problem (as recommended in the Lint documentation):

public class MyClass{

  //static inner class doesn't hold an implicit reference to the outer class
  private static class MyHandler extends Handler {
    //Using a weak reference means you won't prevent garbage collection
    private final WeakReference<MyClass> myClassWeakReference; 

    public MyHandler(MyClass myClassInstance) {
      myClassWeakReference = new WeakReference<MyClass>(myClassInstance);
    }

    @Override
    public void handleMessage(Message msg) {
      MyClass myClass = myClassWeakReference.get();
      if (myClass != null) {
        ...do work here...
      }
    }
  }

  /**
   * An example getter to provide it to some external class
   * or just use 'new MyHandler(this)' if you are using it internally.
   * If you only use it internally you might even want it as final member:
   * private final MyHandler mHandler = new MyHandler(this);
   */
  public Handler getHandler() {
    return new MyHandler(this);
  }
}
skamlet
  • 693
  • 7
  • 23
Sogger
  • 15,962
  • 6
  • 43
  • 40
27

This way worked well for me, keeps code clean by keeping where you handle the message in its own inner class.

The handler you wish to use

Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());

The inner class

class IncomingHandlerCallback implements Handler.Callback{

        @Override
        public boolean handleMessage(Message message) {

            // Handle message code

            return true;
        }
}
dhilt
  • 18,707
  • 8
  • 70
  • 85
Stuart Campbell
  • 1,141
  • 11
  • 13
  • 2
    In here handleMessage method returns true in the end. Could you please explain what exactly this means(the return value true/false)? Thanks. – JibW Aug 15 '13 at 14:32
  • 2
    My understanding of returning true is to indicate that you have handled the message and therefore the message should not be passed anywhere else e.g. the underlying handler. That said I couldn't find any documentation and would be happily corrected. – Stuart Campbell Aug 22 '13 at 09:18
  • 1
    Javadoc says: Constructor associates this handler with the Looper for the current thread and takes a callback interface in which you can handle messages. If this thread does not have a looper, this handler won't be able to receive messages so an exception is thrown. <-- I think the new Handler(new IncomingHandlerCallback()) won't work when there is no Looper attached to the thread, and that CAN be the case. I am not saying its wrong to do so in some cases, I am just saying its not always working as you might expect. – user504342 Oct 20 '13 at 13:07
  • 1
    @StuartCampbell: You are correct. See: https://groups.google.com/forum/#!topic/android-developers/L_xYM0yS6z8 . – Maxwell175 Apr 19 '14 at 20:11
2

With the help of @Sogger's answer, I created a generic Handler:

public class MainThreadHandler<T extends MessageHandler> extends Handler {

    private final WeakReference<T> mInstance;

    public MainThreadHandler(T clazz) {
        // Remove the following line to use the current thread.
        super(Looper.getMainLooper());
        mInstance = new WeakReference<>(clazz);
    }

    @Override
    public void handleMessage(Message msg) {
        T clazz = mInstance.get();
        if (clazz != null) {
            clazz.handleMessage(msg);
        }
    }
}

The interface:

public interface MessageHandler {

    void handleMessage(Message msg);

}

I'm using it as follows. But I'm not 100% sure if this is leak-safe. Maybe someone could comment on this:

public class MyClass implements MessageHandler {

    private static final int DO_IT_MSG = 123;

    private MainThreadHandler<MyClass> mHandler = new MainThreadHandler<>(this);

    private void start() {
        // Do it in 5 seconds.
        mHandler.sendEmptyMessageDelayed(DO_IT_MSG, 5 * 1000);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case DO_IT_MSG:
                doIt();
                break;
        }
    }

    ...

}
mbo
  • 4,611
  • 2
  • 34
  • 54
0

I am not sure but you can try intialising handler to null in onDestroy()

Chaitanya
  • 41
  • 10
  • 1
    Handler objects for the same thread all share a common Looper object, which they post messages to and read from. As messages contain target Handler, as long as there are messages with target handler in the message queue, the handler cannot be garbage collected. – msysmilu Nov 25 '15 at 19:02
0

I'm confused. The example I found avoids the static property entirely and uses the UI thread:

    public class example extends Activity {
        final int HANDLE_FIX_SCREEN = 1000;
        public Handler DBthreadHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                int imsg;
                imsg = msg.what;
                if (imsg == HANDLE_FIX_SCREEN) {
                    doSomething();
                }
            }
        };
    }

The thing I like about this solution is there is no problem trying to mix class and method variables.

0

If you're using Kotlin, simply remove the inner keyword when declaring the nested class.

Nested classes in Kotlin are static by default, declaring them with the inner makes them not-static.

Change your nested Handler subclass declaration from

class myService : Service() {

inner class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}

}

to

class myService : Service() {

class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}

}
M.Ed
  • 969
  • 10
  • 12