Esta clase de controlador debe ser estática o pueden producirse fugas: IncomingHandler

Estoy desarrollando una aplicación Android 2.3.3 con un servicio. Tengo esto dentro de ese servicio para comunicarme con la actividad Principal:

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

Y aquí, el final Messenger mMessenger = new Messenger(new IncomingHandler()); , Recibo la siguiente advertencia de Pelusa:

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

Qué significa eso?

Si la clase IncomingHandler no es estática, tendrá una referencia a su objeto de Service .

Handler objetos del Handler para el mismo subproceso comparten un objeto Looper común, desde el cual publican mensajes y leen.

Como los mensajes contienen el controlador de destino, siempre que haya mensajes con el controlador de destino en la cola de mensajes, el manejador no se puede recolectar como basura. Si el controlador no es estático, su Service o Activity no puede ser recolectado como basura, incluso después de ser destruido.

Esto puede provocar pérdidas de memoria, por lo menos durante un tiempo, siempre y cuando los mensajes permanezcan en la cola. Esto no es un gran problema a menos que publiques mensajes diferidos.

Puede hacer IncomingHandler estático y tener una WeakReference para su servicio:

 static class IncomingHandler extends Handler { private final WeakReference mService; IncomingHandler(UDPListenerService service) { mService = new WeakReference(service); } @Override public void handleMessage(Message msg) { UDPListenerService service = mService.get(); if (service != null) { service.handleMessage(msg); } } } 

Mira esta publicación de Romain Guy para más referencia

Como otros han mencionado, la advertencia de Lint se debe a la posible pérdida de memoria. Puede evitar la advertencia de Lint pasando un Handler.Callback al construir Handler (es decir, no hace una subclase de Handler y no hay Handler class interno no estático):

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

Según lo entiendo, esto no evitará la posible pérdida de memoria. Message objetos de Message contienen una referencia al objeto mIncomingHandler que contiene una referencia al objeto Handler.Callback que contiene una referencia al objeto de Service . Mientras haya mensajes en la cola de mensajes de Looper , el Service no será GC. Sin embargo, no será un problema grave a menos que tenga mensajes de larga espera en la cola de mensajes.

Aquí hay un ejemplo genérico del uso de una clase de controlador débil de referencia y estática para resolver el problema (como se recomienda en la documentación de 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 myClassWeakReference; public MyHandler(MyClass myClassInstance) { myClassWeakReference = new WeakReference(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); } } 

De esta manera funcionó bien para mí, mantiene el código limpio al mantener el lugar donde manejas el mensaje en su propia clase interna.

El controlador que deseas usar

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

La clase interna

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

Con la ayuda de la respuesta de @Sogger, creé un controlador genérico:

 public class MainThreadHandler extends Handler { private final WeakReference 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); } } } 

La interfaz:

 public interface MessageHandler { void handleMessage(Message msg); } 

Lo estoy usando de la siguiente manera. Pero no estoy 100% seguro de si esto es a prueba de fugas. Tal vez alguien podría comentar sobre esto:

 public class MyClass implements MessageHandler { private static final int DO_IT_MSG = 123; private MainThreadHandler 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; } } ... } 

No estoy seguro, pero puedes probar intialising handler para anular en ontroy ()