Diese Handler-Klasse sollte statisch sein oder es könnten Lecks auftreten: IncomingHandler

Lesezeit: 6 Minuten

Diese Handler Klasse sollte statisch sein oder es konnten Lecks auftreten
Vans Fannel

Ich entwickle eine Android 2.3.3-Anwendung mit einem Dienst. Ich habe dies in diesem Dienst, um mit der Hauptaktivität zu kommunizieren:

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

Und hier, final Messenger mMessenger = new Messenger(new IncomingHandler());erhalte ich die folgende Lint-Warnung:

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

Was heißt das?

  • Schau dir das an Blogeintrag für viele weitere Informationen zu diesem Thema!

    – Adrian Mönch

    18. Januar 2013 um 18:15 Uhr

  • Durch Garbage Collection verursachte Speicherlecks … Dies reicht aus, um zu demonstrieren, wie inkonsistent und schlecht designt Java ist

    – Gojir4

    6. Juni 2018 um 11:30 Uhr

1646528589 214 Diese Handler Klasse sollte statisch sein oder es konnten Lecks auftreten
Tomasz Niedabylski

Wenn IncomingHandler Klasse ist nicht statisch, sie wird einen Verweis auf Ihre haben Service Objekt.

Handler Objekte für denselben Thread teilen sich alle ein gemeinsames Looper-Objekt, an das sie Nachrichten senden und von dem sie lesen.

Als Nachrichten Ziel enthalten Handler, solange Nachrichten mit Zielhandler in der Nachrichtenwarteschlange vorhanden sind, kann der Handler nicht von der Garbage Collection erfasst werden. Wenn der Handler nicht statisch ist, ist Ihr Service oder Activity kann nicht müllgesammelt werden, selbst nachdem es zerstört wurde.

Dies kann zumindest für einige Zeit zu Speicherlecks führen – solange die Nachrichten in der Warteschlange bleiben. Dies ist kein großes Problem, es sei denn, Sie posten lange verspätete Nachrichten.

Du kannst das schaffen IncomingHandler statisch und haben a WeakReference zu Ihren Diensten:

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

Sieh dir das an Post von Romain Guy für weitere Referenzen

  • Romain zeigt, dass die WeakReference auf die äußere Klasse alles ist, was benötigt wird – eine statische verschachtelte Klasse ist nicht erforderlich. Ich denke, ich werde den WeakReference-Ansatz bevorzugen, da sich sonst die gesamte äußere Klasse aufgrund all der “statischen” Variablen, die ich benötigen würde, drastisch ändert.

    – Irgendwer irgendwo

    18. Juli 2012 um 19:07 Uhr

  • Wenn Sie eine verschachtelte Klasse verwenden möchten, muss diese statisch sein. Ansonsten ändert WeakReference nichts. Die innere (verschachtelte, aber nicht statische) Klasse enthält immer einen starken Bezug zur äußeren Klasse. Es sind jedoch keine statischen Variablen erforderlich.

    – Tomasz Niedabylski

    18. Juli 2012 um 21:24 Uhr


  • @SomeoneSomewhere mService ist eine WeakReference. get() gibt null zurück, wenn das referenzierte Objekt gc-ed wurde. In diesem Fall, wenn der Dienst tot ist.

    – Tomasz Niedabylski

    23. Juli 2012 um 23:02 Uhr

  • Hinweis: Nachdem ich den IncomingHandler statisch gemacht hatte, erhielt ich den Fehler „Der Konstruktor MyActivity.IncomingHandler() ist nicht definiert.“ in der Zeile “final Messenger inMessenger = new Messenger(new IncomingHandler());”. Die Lösung besteht darin, diese Zeile in “final Messenger inMessenger = new Messenger(new IncomingHandler(this));” zu ändern.

    – Lanze Lefebure

    5. Februar 2013 um 3:18 Uhr


  • @Someone Somewhere Ja, Romains Beitrag ist insofern falsch, als er es verpasst hat, die Statik der inneren Klasse zu deklarieren, die den ganzen Punkt verfehlt. Es sei denn, er hat einen ultracoolen Compiler, der innere Klassen automatisch in statische Klassen umwandelt, wenn sie keine Klassenvariablen verwenden.

    – Sogger

    7. Januar 2015 um 18:22 Uhr

Diese Handler Klasse sollte statisch sein oder es konnten Lecks auftreten
Michael

Wie andere bereits erwähnt haben, ist die Lint-Warnung auf das potenzielle Speicherleck zurückzuführen. Sie können die Lint-Warnung vermeiden, indem Sie a übergeben Handler.Callback beim konstruieren Handler (dh Sie machen keine Unterklassen Handler und es gibt keine Handler nichtstatische innere Klasse):

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

So wie ich es verstehe, wird dies das potenzielle Speicherleck nicht vermeiden. Message Objekte enthalten einen Verweis auf die mIncomingHandler Objekt, das eine Referenz enthält Handler.Callback Objekt, das einen Verweis auf die enthält Service Objekt. Solange es Nachrichten in der gibt Looper Nachrichtenwarteschlange, die Service wird nicht GC sein. Dies ist jedoch kein ernsthaftes Problem, es sei denn, Sie haben Nachrichten mit langer Verzögerung in der Nachrichtenwarteschlange.

  • @Braj Ich denke nicht, dass es eine gute Lösung ist, die Fusselwarnung zu vermeiden, aber den Fehler trotzdem beizubehalten. Es sei denn, wie die Lint-Warnung besagt, wenn der Handler nicht auf Ihren Hauptlooper gesetzt wird (und Sie sicherstellen können, dass alle anstehenden Nachrichten zerstört werden, wenn die Klasse zerstört wird), dann wird das Lecken der Referenz gemildert.

    – Sogger

    7. Januar 2015 um 18:29 Uhr

1646528591 225 Diese Handler Klasse sollte statisch sein oder es konnten Lecks auftreten
Sogger

Hier ist ein allgemeines Beispiel für die Verwendung einer schwachen Referenz und einer statischen Handler-Klasse, um das Problem zu lösen (wie in der Lint-Dokumentation empfohlen):

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

  • Sogger Beispiel ist großartig. Allerdings ist die letzte Methode in Myclass sollte als deklariert werden public Handler getHandler() anstatt public void

    – Jason Porter

    11. März 2015 um 22:36 Uhr

  • Es ähnelt einer Antwort von Tomasz Niedabylski

    – CoolMind

    12. August 2015 um 17:51 Uhr


1646528592 958 Diese Handler Klasse sollte statisch sein oder es konnten Lecks auftreten
Stuart Campell

Dieser Weg hat für mich gut funktioniert, hält den Code sauber, indem er behält, wo Sie die Nachricht in seiner eigenen inneren Klasse behandeln.

Der Handler, den Sie verwenden möchten

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

Die innere Klasse

class IncomingHandlerCallback implements Handler.Callback{

        @Override
        public boolean handleMessage(Message message) {

            // Handle message code

            return true;
        }
}

Mit Hilfe der Antwort von @Sogger habe ich einen generischen Handler erstellt:

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

Die Schnittstelle:

public interface MessageHandler {

    void handleMessage(Message msg);

}

Ich benutze es wie folgt. Aber ich bin mir nicht 100% sicher, ob das auslaufsicher ist. Vielleicht kann sich jemand dazu äußern:

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

    ...

}

1646528593 596 Diese Handler Klasse sollte statisch sein oder es konnten Lecks auftreten
Chaitanya

Ich bin mir nicht sicher, aber Sie können versuchen, den Handler in onDestroy () auf null zu initialisieren

1646528593 488 Diese Handler Klasse sollte statisch sein oder es konnten Lecks auftreten
Benutzer2515235

Ich bin verwirrt. Das Beispiel, das ich gefunden habe, vermeidet die statische Eigenschaft vollständig und verwendet den UI-Thread:

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

Was ich an dieser Lösung mag, ist, dass es kein Problem gibt, Klassen- und Methodenvariablen zu mischen.

951390cookie-checkDiese Handler-Klasse sollte statisch sein oder es könnten Lecks auftreten: IncomingHandler

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy