此Handler类应该是静态的,否则可能会发生泄漏:IncomingHandler

时间:2012-07-10 06:42:50

标签: android memory-leaks static-classes android-lint android-handler

我正在开发一个带有服务的Android 2.3.3应用程序。我在服务中有这个与主要活动沟通:

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());
    [ ... ]
}

在这里final Messenger mMessenger = new Messenger(new IncomingHandler());,我收到以下Lint警告:

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

这是什么意思?

7 个答案:

答案 0 :(得分:372)

如果IncomingHandler类不是静态的,它将引用您的Service对象。

同一个线程的

Handler个对象共享一个共同的Looper对象,它们将消息发送到该对象并从中读取。

由于消息包含目标Handler,只要消息队列中存在带有目标处理程序的消息,就不能对处理程序进行垃圾回收。如果处理程序不是静态的,即使被销毁,您的ServiceActivity也无法进行垃圾回收。

这可能会导致内存泄漏,至少在一段时间内 - 只要消息留在队列中。除非您发布长时间延迟的消息,否则这不是什么大问题。

您可以将IncomingHandler设为静态,并为您的服务设置WeakReference

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);
         }
    }
}

请参阅Romain Guy的post 以获取进一步的参考资料

答案 1 :(得分:61)

正如其他人提到的,Lint警告是因为潜在的内存泄漏。您可以在构建Handler.Callback时传递Handler来避免Lint警告(即,您不是Handler的子类,并且没有Handler非静态内部类:

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

据我了解,这不会避免潜在的内存泄漏。 Message个对象包含对mIncomingHandler对象的引用,该对象包含对Handler.Callback对象的引用,该对象包含对Service对象的引用。只要Looper消息队列中有消息,Service就不会是GC。但是,除非您在消息队列中有很长的延迟消息,否则它不会是一个严重的问题。

答案 2 :(得分:30)

以下是使用弱引用和静态处理程序类来解决问题的一般示例(如Lint文档中所建议的那样):

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);
  }
}

答案 3 :(得分:23)

这种方式对我来说效果很好,通过保留在自己的内部类中处理消息的位置来保持代码清洁。

您希望使用的处理程序

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

内在类

class IncomingHandlerCallback implements Handler.Callback{

        @Override
        public boolean handleMessage(Message message) {

            // Handle message code

            return true;
        }
}

答案 4 :(得分:1)

在@Sogger的回答的帮助下,我创建了一个通用的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);
        }
    }
}

界面:

public interface MessageHandler {

    void handleMessage(Message msg);

}

我按如下方式使用它。但我不能100%确定这是否是泄漏安全的。也许有人可以对此发表评论:

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;
        }
    }

    ...

}

答案 5 :(得分:0)

我不确定但您可以尝试在onDestroy()

中初始化处理程序为null

答案 6 :(得分:0)

我很困惑。 我发现的示例完全避免使用static属性,而是使用UI线程:

    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();
                }
            }
        };
    }

我喜欢这种解决方案的地方是尝试混合类和方法变量没有问题。