开发者

How/when is a Handler garbage collected?

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.ht开发者_开发技巧ml

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?


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


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.


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"?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜