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

Van*_*nel 287 android memory-leaks static-classes android-lint android-handler

我正在开发一个带有服务的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());
    [ ... ]
}
Run Code Online (Sandbox Code Playgroud)

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

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

这是什么意思?

Tom*_*ski 384

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

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

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

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

您可以制作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);
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅Romain Guy的这篇文章以获得进一步的参考

  • 如果要使用嵌套类,则必须是静态的.否则,WeakReference不会改变任何东西.内部(嵌套但非静态)类始终拥有对外部类的强引用.但是,不需要任何静态变量. (33认同)
  • @Someone Somewhere是的,Romain的帖子是错误的,因为他错过了宣告内部阶级静态而忽略了整个观点.除非他有一些超酷的编译器,当它们不使用类变量时会自动将内部类转换为静态类. (4认同)
  • Romain显示外部类的WeakReference是所需要的 - 静态嵌套类不是必需的.我想我会更喜欢WeakReference方法,因为否则整个外部类会因为我需要的所有"静态"变量而急剧变化. (3认同)
  • @SomeoneSomewhere mSerivce是一个WeakReference.当引用的对象是gc-ed时,`get()`将返回null.在这种情况下,服务已经死亡. (2认同)
  • 你必须扩展它 (2认同)
  • 注意:将 IncomingHandler 设为静态后,我收到错误“构造函数 MyActivity.IncomingHandler() 未定义”。在“final Messenger inMessenger = new Messenger(new IncomingHandler());”行上。解决方案是将该行更改为“final Messenger inMessenger = new Messenger(new IncomingHandler(this));”。 (2认同)

Mic*_*ael 65

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

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

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

  • @Braj我不认为避免lint警告,但仍然保持错误是一个很好的解决方案.除非,因为lint警告声明处理程序没有放在你的主循环器上(并且你可以确保在类被销毁时销毁它上面的所有消息),所以减少了泄漏引用. (9认同)
  • 完美解决方案 (2认同)

Sog*_*ger 32

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

  • Sogger的例子很棒.但是,`Myclass`中的最后一个方法应该声明为`public Handler getHandler()`而不是`public void` (2认同)

Stu*_*ell 24

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

您要使用的处理程序

Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());
Run Code Online (Sandbox Code Playgroud)

内在的阶级

class IncomingHandlerCallback implements Handler.Callback{

        @Override
        public boolean handleMessage(Message message) {

            // Handle message code

            return true;
        }
}
Run Code Online (Sandbox Code Playgroud)

  • 在这里handleMessage方法最后返回true.你能解释一下这究竟是什么意思(返回值是真/假)吗?谢谢. (2认同)
  • 我对返回true的理解是指示您已经处理了消息,因此消息不应该传递给其他任何地方,例如底层处理程序.那说我找不到任何文件,并乐意纠正. (2认同)