首页 文章

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

提问于
浏览
275

我正在开发一个带有服务的Android 2.3.3应用程序 . 我在服务中有这个与Main活动进行通信:

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

这是什么意思?

6 回答

  • 370

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

    您要使用的处理程序

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

    内在的阶级

    class IncomingHandlerCallback implements Handler.Callback{
    
            @Override
            public boolean handleMessage(Message message) {
    
                // Handle message code
    
                return true;
            }
    }
    
  • 22

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

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

    由于消息包含目标 Handler ,只要消息队列中存在具有目标处理程序的消息,就不能对处理程序进行垃圾回收 . 如果处理程序不是静态的,那么即使在被销毁之后,您的 Service 或_2615550也不会被垃圾收集 .

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

    您可以将 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

    正如其他人所提到的,Lint警告是因为潜在的内存泄漏 . 您可以通过在构造 Handler 时传递_2615555来避免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 . 但是,除非您在消息队列中有长延迟消息,否则它不会是一个严重的问题 .

  • 0

    下面是使用弱引用和静态处理程序类来解决问题的一般示例(如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);
      }
    }
    
  • 58

    在@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;
            }
        }
    
        ...
    
    }
    
  • 30

    我不确定但是你可以在onDestroy()中尝试初始化处理程序为null

相关问题