如何/何时收集处理程序垃圾?

Rol*_*and 16 android

在我的一类中,我有以下代码:

mHandler = createHandler();

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

文件说:

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

每个Handler实例都与一个线程和该线程的消息队列相关联

因此,如果我理解正确,只要应用程序线程正在运行,Handler就不会被垃圾收集,这是正确的吗?

在我的具体示例中,由于Handler是一个匿名内部类,因此它具有对封闭Object的隐式引用以及由其指向的对象的整个层次结构.这看起来像是一个内存泄漏的配方.

顺便说一句,我可以让处理程序停止发送消息(这就是为什么我有if (!paused))但这不会使它成为GCed,对吧?

那么有没有办法从消息队列中删除Handler并使其成为GCed?

Joe*_*eer 14

对Handler源的检查揭示了更多细节.

以下是Romain Guy添加的Handler()构造函数中的一些调试代码:

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());
  }
}
Run Code Online (Sandbox Code Playgroud)

警告很明确:不要将Handler子类声明为内部类.

Handler的looper是从静态ThreadLocal实例获得的:

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();
}
Run Code Online (Sandbox Code Playgroud)

泄漏剖析:

主应用程序线程保留Looper及其MessageQueue,队列中的Messages保留到其目标Handler的链接,Handler - 除非它是一个带有WeakReference的静态嵌套类 - 将保留您的Activity及其观点.

您可以通过清理消息来尝试插入此泄漏:

handler.removeMessages(what);
Run Code Online (Sandbox Code Playgroud)

但这说起来容易做起来难.

另请参阅Java和Android中的内存泄漏


Ste*_*n C 4

在我的具体示例中,由于 Handler 是一个匿名内部类,因此它具有对封闭对象及其所指向的整个对象层次结构的隐式引用。

static通过使用嵌套类而不是匿名内部类,您可以将潜在泄漏的影响减少到几乎没有。