android - tesis - Esta clase de manejador debe ser estática o podrían producirse fugas:
tesis sobre youtube pdf (6)
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í, final Messenger mMessenger = new Messenger(new IncomingHandler());
, Me sale el siguiente aviso de Pelusa:
This Handler class should be static or leaks might occur: IncomingHandler
Qué significa eso?
Aquí hay un ejemplo genérico de usar una referencia débil y una clase de manejador estático 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<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);
}
}
Como han mencionado otros, la advertencia de Pelusa se debe a la posible pérdida de memoria. Puede evitar la advertencia de Lint pasando un Handler.Callback
al construir el Handler
(es decir, no hace subclase del Handler
y no hay un Handler
no interno de clase):
Handler mIncomingHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// todo
return true;
}
});
Como lo entiendo, esto no evitará la pérdida potencial 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 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 demora prolongados en la cola de mensajes.
Con la ayuda de la respuesta de @Sogger, creé un controlador genérico:
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);
}
}
}
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 seguro contra fugas. Tal vez alguien podría comentar sobre esto:
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;
}
}
...
}
De esta manera, funcionó bien para mí, mantiene el código limpio al mantener el lugar donde maneja el mensaje en su propia clase interna.
El controlador que desea utilizar
Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());
La clase interior
class IncomingHandlerCallback implements Handler.Callback{
@Override
public boolean handleMessage(Message message) {
// Handle message code
return true;
}
}
No estoy seguro, pero puedes intentar inicializar el manejador a nulo en onDestroy ()
Si la clase IncomingHandler
no es estática, tendrá una referencia a su objeto de Service
.
Handler
objetos de Handler
para el mismo hilo comparten un objeto Looper común, al que envían 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 controlador no puede ser recolectado como basura. Si el controlador no es estático, su Service
o Activity
no puede ser recogida de basura, incluso después de ser destruida.
Esto puede provocar pérdidas de memoria, al menos durante algún tiempo, siempre que los mensajes permanezcan en la cola. Esto no es un gran problema a menos que publique mensajes con mucho retraso.
Puedes hacer que IncomingHandler
estático y tener una WeakReference
a tu servicio:
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);
}
}
}
Ver este post por Romain Guy para mayor referencia