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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11407943/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:18:03  来源:igfitidea点击:

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

androidmemory-leaksstatic-classesandroid-lintandroid-handler

提问by VansFannel

I'm developing an Android 2.3.3 application with a service. I have this inside that service to communicate with Main activity:

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

And here, final Messenger mMessenger = new Messenger(new IncomingHandler());, I get the following Lint warning:

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

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

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

What does it mean?

这是什么意思?

回答by Tomasz Niedabylski

If IncomingHandlerclass is not static, it will have a reference to your Serviceobject.

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

Handlerobjects for the same thread all share a common Looper object, which they post messages to and read from.

Handler同一线程的对象都共享一个公共的 Looper 对象,它们向其发送消息并从中读取消息。

As messages contain target Handler, as long as there are messages with target handler in the message queue, the handler cannot be garbage collected. If handler is not static, your Serviceor Activitycannot be garbage collected, even after being destroyed.

由于消息中包含 target Handler,只要消息队列中有带有 target 处理程序的消息,处理程序就不会被垃圾回收。如果处理程序不是静态的,您的ServiceActivity不能被垃圾收集,即使在被销毁后也是如此。

This may lead to memory leaks, for some time at least - as long as the messages stay int the queue. This is not much of an issue unless you post long delayed messages.

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

You can make IncomingHandlerstatic and have a WeakReferenceto your service:

您可以使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);
         }
    }
}

See this post by Romain Guy for further reference

请参阅Romain Guy 的这篇文章以获取更多参考

回答by Michael

As others have mentioned the Lint warning is because of the potential memory leak. You can avoid the Lint warning by passing a Handler.Callbackwhen constructing Handler(i.e. you don't subclass Handlerand there is no Handlernon-static inner class):

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

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

As I understand it, this will not avoid the potential memory leak. Messageobjects hold a reference to the mIncomingHandlerobject which holds a reference the Handler.Callbackobject which holds a reference to the Serviceobject. As long as there are messages in the Loopermessage queue, the Servicewill not be GC. However, it won't be a serious issue unless you have long delay messages in the message queue.

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

回答by Sogger

Here is a generic example of using a weak reference and static handler class to resolve the problem (as recommended in the Lint documentation):

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

回答by Stuart Campbell

This way worked well for me, keeps code clean by keeping where you handle the message in its own inner class.

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

The handler you wish to use

您希望使用的处理程序

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

The inner class

内部类

class IncomingHandlerCallback implements Handler.Callback{

        @Override
        public boolean handleMessage(Message message) {

            // Handle message code

            return true;
        }
}

回答by Marius

With the help of @Sogger's answer, I created a generic Handler:

在@Sogger 的回答的帮助下,我创建了一个通用的处理程序:

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

The interface:

界面:

public interface MessageHandler {

    void handleMessage(Message msg);

}

I'm using it as follows. But I'm not 100% sure if this is leak-safe. Maybe someone could comment on this:

我正在使用它如下。但我不是 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;
        }
    }

    ...

}

回答by user2515235

I'm confused. The example I found avoids the static property entirely and uses the UI thread:

我糊涂了。我发现的示例完全避免了静态属性并使用了 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();
                }
            }
        };
    }

The thing I like about this solution is there is no problem trying to mix class and method variables.

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

回答by Chaitanya

I am not sure but you can try intialising handler to null in onDestroy()

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