Je développe une application Android 2.3.3 avec un service. J'ai ceci à l'intérieur de ce service pour communiquer avec l'activité principale:
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());
[ ... ]
}
Et ici, final Messenger mMessenger = new Messenger(new IncomingHandler());
, je reçois l'avertissement suivant:
This Handler class should be static or leaks might occur: IncomingHandler
Qu'est-ce que ça veut dire?
Si la classe IncomingHandler
n'est pas statique, elle fera référence à votre objet Service
.
Les objets Handler
du même thread partagent tous un objet Looper commun, sur lequel ils publient des messages et les lisent.
Comme les messages contiennent la cible Handler
, tant qu'il y a des messages avec le gestionnaire cible dans la file d'attente, le gestionnaire ne peut pas être nettoyé. Si le gestionnaire n'est pas statique, votre Service
ou Activity
ne peut pas être nettoyé, même après avoir été détruit.
Cela peut entraîner des fuites de mémoire, du moins pendant un certain temps, à condition que les messages restent dans la file d'attente. Ce n’est pas un problème si vous ne postez pas de longs messages différés.
Vous pouvez rendre IncomingHandler
statique et avoir un WeakReference
à votre service:
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 par Romain Guy pour plus de références
Comme d'autres l'ont mentionné, l'avertissement Lint est dû à la fuite de mémoire potentielle. Vous pouvez éviter l'avertissement Lint en passant un Handler.Callback
lors de la construction de Handler
(c'est-à-dire que vous ne sous-classe pas Handler
et qu'il n'y a pas de Handler
classe interne non statique):
Handler mIncomingHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// todo
return true;
}
});
Si j'ai bien compris, cela n'évitera pas la fuite de mémoire potentielle. Les objets Message
contiennent une référence à l'objet mIncomingHandler
qui contient une référence à l'objet Handler.Callback
qui contient une référence à l'objet Service
. Tant qu'il y aura des messages dans la file d'attente de messages Looper
, le Service
ne sera pas GC. Toutefois, ce ne sera pas un problème grave, sauf si vous avez des messages avec un retard important dans la file d'attente.
Voici un exemple générique d'utilisation d'une référence faible et d'une classe de gestionnaire statique pour résoudre le problème (comme recommandé dans la documentation 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);
}
}
Cette méthode a bien fonctionné pour moi, maintient le code propre en conservant le lieu où vous gérez le message dans sa propre classe interne.
Le gestionnaire que vous souhaitez utiliser
Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());
La classe intérieure
class IncomingHandlerCallback implements Handler.Callback{
@Override
public boolean handleMessage(Message message) {
// Handle message code
return true;
}
}
Avec l'aide de la réponse de @ Sogger, j'ai créé un gestionnaire générique:
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);
}
}
}
L'interface:
public interface MessageHandler {
void handleMessage(Message msg);
}
Je l'utilise comme suit. Mais je ne suis pas sûr à 100% s'il s'agit d'une fuite sans danger. Peut-être que quelqu'un pourrait commenter ceci:
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;
}
}
...
}
Je ne suis pas sûr mais vous pouvez essayer le gestionnaire d'initialisation pour annuler null dans onDestroy ()