20

Inside a class of mine I have the following code:

mHandler = createHandler();

private Handler createHandler() {
    return new Handler() {
        public void handleMessage (Message msg) {
            update();
            if (!paused) {
                sendEmptyMessageDelayed(0, 300);
            }
        }
    };
}

The documentation says:

http://developer.android.com/reference/android/os/Handler.html

Each Handler instance is associated with a single thread and that thread's message queue

So if I understood correctly the Handler is not garbage collected as long as the application thread is running, is that correct?

In my specific example since the Handler is an anonymous inner class it has an implicit reference to the enclosing Object and the whole hierarchy of objects that is pointed by it. This looks to me like a recipe for memory leaking.

Btw, I can make the Handler stop sending messages(that's why I have the if (!paused)) but this won't make it be GCed, right?

So is there a way to remove the Handler from the message queue and get it to be GCed?

Roland
  • 7,525
  • 13
  • 61
  • 124

3 Answers3

15

An examination of the Handler source reveals more details.

Here is some debug code from the Handler() constructor that was added by Romain Guy:

if (FIND_POTENTIAL_LEAKS) {
  final Class<? extends Handler> klass = getClass();
  if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
      (klass.getModifiers() & Modifier.STATIC) == 0) {
    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
      klass.getCanonicalName());
  }
}

The warning is clear: Don't declare your Handler subclass as an inner class.

The Handler's looper is obtained from a static ThreadLocal instance:

mLooper = Looper.myLooper();

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static final Looper myLooper() {
    return (Looper)sThreadLocal.get();
}

Anatomy of the leak:

The main app thread retains the Looper and its MessageQueue, the Messages in the queue retain a link to their target Handler, and the Handler -- unless it is a static nested class with a WeakReference to your Activity -- will retain your Activity and its views.

You could instead try to plug this leak by cleaning up your messages:

handler.removeMessages(what);

but this is easier said than done.

Also see On Memory Leaks in Java and in Android

Joe Bowbeer
  • 3,574
  • 3
  • 36
  • 47
4

In my specific example since the Handler is an anonymous inner class it has an implicit reference to the enclosing Object and the whole hierarchy of objects that is pointed by it.

You could reduce the impact of the potential leak to almost nothing by using a static nested class instead of an anonymous inner class.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 1
    Thanks, this was the best answer and practically solves my problem. But I still consider it a design weakness of Android if it is not possible to remove a Handler once it is created. – Roland Feb 24 '11 at 20:18
0

No, stop sending message doesn't make GC work. As the doc points out, it bounds to the thread which creating it. If thread is running, the handler won't be recycled by GC.

Why do you think this could cause memory leaking? What do you mean by "implicit reference to the enclosing Object"?

Sarstime
  • 184
  • 1
  • 3
  • 14
  • @Sarstine - any non-static inner class has an implicit reference to an instance of its enclosing class so that it can use the enclosing classes instance methods and attributes. – Stephen C Feb 24 '11 at 01:48
  • True, the implicit reference does keep some memory alive , but this is not a memory leak. The outer object is kept in memory because the inner object can still use it; indeed, if you don't need it, declare the inner class static. In the end, the outer class is still reachable. It would be a memory leak if the outer class would be unreachable, and still remain in memory. For instanced, by some circular reference, which is what the Island of Isolation. Garbage Collector could handle this situation already. – Sarstime Feb 24 '11 at 02:02
  • In my case it is a memory leak because I no longer have any use for the outer object and in fact I want to get rid of it. The problem is that the Handler still has a reference to it and the Thread has a reference to the Handler and there is no way to get rid of the latter reference(unless there is an answer to my original question). – Roland Feb 24 '11 at 17:08