In der gängigen Android-Programmierung wird Handler häufig verwendet, wenn asynchrone Vorgänge ausgeführt und zurückgegebene Ergebnisse verarbeitet werden. Normalerweise wird unser Code so implementiert.
Kopieren Sie den Codecode wie folgt:
Die öffentliche Klasse SampleActivity erweitert die Aktivität {
privater finaler Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
}
Tatsächlich kann der obige Code jedoch zu Speicherverlusten führen. Wenn Sie das Android-Lint-Tool verwenden, erhalten Sie eine solche Warnung.
Kopieren Sie den Codecode wie folgt:
In Android sollten Handler-Klassen statisch sein, da sonst Lecks auftreten können. Nachrichten, die in die MessageQueue des Anwendungsthreads eingereiht sind, behalten auch ihren Ziel-Handler bei. Wenn der Handler eine innere Klasse ist, wird auch seine äußere Klasse beibehalten, um ein Leck der äußeren Klasse zu vermeiden. Deklarieren Sie den Handler als statische verschachtelte Klasse mit einer WeakReference auf seine äußere Klasse
Wenn Sie dies sehen, sind Sie möglicherweise immer noch verwirrt. Wo im Code kann es zu einem Speicherverlust kommen und wie kann es zu einem Speicherverlust kommen? Dann analysieren wir es langsam.
1. Wenn eine Android-Anwendung gestartet wird, wird automatisch eine Looper-Instanz zur Verwendung durch den Hauptthread der Anwendung erstellt. Die Hauptaufgabe von Looper besteht darin, die Nachrichtenobjekte in der Nachrichtenwarteschlange einzeln zu verarbeiten. In Android werden alle Ereignisse des Android-Frameworks (z. B. Aufrufe von Aktivitätslebenszyklusmethoden und Schaltflächenklicks usw.) in Nachrichten eingefügt und dann zur Nachrichtenwarteschlange hinzugefügt, um von Looper verarbeitet zu werden. Looper ist für die Verarbeitung dieser Ereignisse verantwortlich um eins. Der Lebenszyklus des Loopers im Hauptthread ist so lang wie die aktuelle Anwendung.
2. Wenn ein Handler im Hauptthread initialisiert wird und wir eine auf diesen Handler ausgerichtete Nachricht an die von Looper verarbeitete Nachrichtenwarteschlange senden, enthält die tatsächlich gesendete Nachricht bereits einen Verweis auf eine Handler-Instanz. Nur auf diese Weise wird die Looper-Verarbeitung ausgeführt Wenn die Nachricht empfangen wird, kann Handler#handleMessage(Message) aufgerufen werden, um die korrekte Verarbeitung der Nachricht abzuschließen.
3. In Java enthalten nicht statische innere Klassen und anonyme innere Klassen implizit Verweise auf ihre äußeren Klassen. Statische innere Klassen enthalten keine Verweise auf äußere Klassen. Weitere Informationen hierzu finden Sie unter Chat Java: „Ungültiger“ privater Modifikator
Es stimmt, dass es im obigen Codebeispiel etwas schwierig ist, Speicherverluste zu erkennen. Das folgende Beispiel ist jedoch sehr offensichtlich
Kopieren Sie den Codecode wie folgt:
Die öffentliche Klasse SampleActivity erweitert die Aktivität {
privater finaler Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savingInstanceState) {
super.onCreate(savedInstanceState);
// Eine Nachricht posten und deren Ausführung um 10 Minuten verzögern.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);
// Zurück zur vorherigen Aktivität.
beenden();
}
}
Wenn wir den obigen Code analysieren, wird beim Ausführen der Abschlussmethode der Aktivität festgestellt, dass die verzögerte Nachricht 10 Minuten lang in der Nachrichtenwarteschlange des Hauptthreads vorhanden ist, bevor sie verarbeitet wird. Diese Nachricht enthält einen Verweis auf den Handler, und der Handler ist eine anonyme Instanz von Die innere Klasse enthält einen Verweis auf die externe SampleActivity, was dazu führt, dass die SampleActivity nicht recycelt werden kann. Daher können viele von der SampleActivity gehaltene Ressourcen nicht recycelt werden. Dies wird oft als Speicherverlust bezeichnet.
Beachten Sie, dass das neue Runnable oben auch von einer anonymen inneren Klasse implementiert wird. Es enthält auch einen Verweis auf SampleActivity und verhindert, dass SampleActivity recycelt wird.
Um dieses Problem zu lösen, besteht die Idee darin, keine nicht statischen inneren Klassen zu verwenden. Wenn Sie den Handler erben, platzieren Sie ihn entweder in einer separaten Klassendatei oder verwenden Sie eine statische innere Klasse. Da die statische innere Klasse keinen Verweis auf die äußere Klasse enthält, führt dies nicht zu Speicherverlusten in der Instanz der äußeren Klasse. Wenn Sie eine externe Aktivität in einer statischen inneren Klasse aufrufen müssen, können wir schwache Referenzen verwenden, um damit umzugehen. Darüber hinaus muss Runnable auch als statisches Mitgliedsattribut festgelegt werden. Hinweis: Eine statische anonyme Instanz einer inneren Klasse enthält keinen Verweis auf die äußere Klasse. Der Code, der nach der Änderung keinen Speicherverlust verursacht, lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
Die öffentliche Klasse SampleActivity erweitert die Aktivität {
/**
* Instanzen statischer innerer Klassen enthalten keine implizite
* Verweis auf ihre äußere Klasse.
*/
private statische Klasse MyHandler erweitert Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity-Aktivität) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity-Aktivität = mActivity.get();
if (aktivität != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instanzen anonymer Klassen enthalten kein implizites
* Verweis auf ihre äußere Klasse, wenn sie „statisch“ sind.
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savingInstanceState) {
super.onCreate(savedInstanceState);
// Eine Nachricht posten und deren Ausführung um 10 Minuten verzögern.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Zurück zur vorherigen Aktivität.
beenden();
}
}
Tatsächlich werden viele Speicherverluste in Android durch die Verwendung nicht statischer interner Klassen in der Aktivität verursacht, wie in diesem Artikel erwähnt. Daher müssen wir bei der Verwendung nicht statischer interner Klassen besondere Aufmerksamkeit auf sich ziehen Ist das gehaltene Objekt größer als sein äußeres Klassenobjekt, kann es zu Speicherverlusten kommen. Persönlich tendiere ich dazu, die statische Klasse und die schwachen Referenzmethoden des Artikels zu verwenden, um dieses Problem zu lösen.