Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How/when is a Handler garbage collected?

Tags:

android

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?

like image 666
Roland Avatar asked Feb 24 '11 00:02

Roland


1 Answers

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

like image 116
Joe Bowbeer Avatar answered Oct 15 '22 03:10

Joe Bowbeer