2016-02-20 10 views
17

Ich entschied, dass es höchste Zeit war, Leak Canary zu verwenden, um Lecks in meinen Apps zu erkennen, und wie immer, versuchte ich es in meinem Projekt zu implementieren, um wirklich zu verstehen, wie man das verwendet Werkzeug. Die Umsetzung war einfach, der schwierige Teil war zu lesen, was das Werkzeug auf mich zurückwirft. Ich habe eine Scrollview, die Speicher im Speichermanager ansammeln scheint, während ich scroll rauf und runter (obwohl es keine neuen Daten lädt), also dachte ich, das war ein gutes Kandidat Objekt auf Lecks zu verfolgen, das ist das Ergebnis:Leak Canary, Recyclerview undicht Adapter

enter image description here

Es sieht aus wie v7.widget.RecyclerView den Adapter undicht ist, und nicht meine Anwendung. Aber das kann nicht stimmen .... oder?

Hier ist der Code für den Adapter und die Klasse macht Gebrauch davon: https://gist.github.com/feresr/a53c7b68145d6414c40ec70b3b842f1e

ich eine Prämie für diese Frage begonnen, weil es

+0

Es sieht so aus, als würden Sie den Anwendungskontext übergeben, wenn Sie wahrscheinlich entweder den Kontext von RecyclerView oder den Kontext Ihrer Aktivitäten verwenden sollten. Anwendungskontexte sind langlebig, was eine Sammlung verhindern würde. – Submersed

Antwort

6

Wenn der Adapter länger lebt als die RecyclerView tut, müssen Sie die Adapterreferenz in onDestroyView löschen:

@Override 
public void onDestroyView() { 
    recyclerView.setAdapter(null); 
    super.onDestroyView(); 
} 

Andernfalls wird der Adapter einen Verweis auf die RecyclerView enthalten, die bereits nicht mehr im Speicher vorhanden sein sollte.

+0

Dies ist, was ich getan habe, und in der Tat behebt das Problem. Ich habe die gleiche Antwort in einer anderen SO-Frage gesehen. Ich würde gerne verstehen, warum dies der Fall ist. Die Referenzkette sollte Fragment -> Recyclerview -> Adapter sein. Wenn also das Fragment nicht mehr auf die Recyclerview verweist, sollten sowohl die Recyclerview als auch deren Adapter als Garbage Collections erfasst werden. – feresr

+1

Wenn Sie einen Adapter für eine 'RecyclerView' mit' RecyclerView.setAdapter' einstellen, wird dieser Adapter die 'RecyclerView' als' AdapterDataObserver' registrieren und eine Referenz darauf in einer internen Liste halten. Wenn der Adapter länger als das 'RecyclerView' (was der Fall ist, wenn Sie den Adapter zum Beispiel in' onCreate' eines 'Fragments' definieren), wird er einen Verweis auf ein' RecyclerView' enthalten, das GC'ed sein sollte stattdessen. Der Aufruf von 'recyclerView.setAdapter (null)' hebt die Registrierung des 'RecyclerView' mit diesem Adapter auf. –

4

Vor allem nach zwei Jahren auf eine ganz andere Anwendung wieder aufgetaucht, Ich verweise auf this file.

Es sieht aus wie v7.widget.RecyclerView ist undicht den Adapter, und nicht meine Anwendung. Aber das kann nicht stimmen .... oder?

Es ist eigentlich Ihr Adapter, der die RecyclerView undicht ist (und es ist ziemlich klar, durch die Spur grafische Darstellung und den Titel der LeakCanary Aktivität gemacht). Ich bin mir jedoch nicht sicher, ob es sich um den "übergeordneten" RecyclerView oder den verschachtelten im HourlyViewHolder oder um beides handelt. Ich denke, die Täter sind Ihre ViewHolders. Indem Sie sie zu nicht statischen inneren Klassen machen, geben Sie ihnen explizit den Verweis auf die umschließende Adapterklasse, und dies verbindet den Adapter nahezu direkt mit den wiederverwerteten Ansichten, da das übergeordnete Element jedes RecallViews in Ihren Besitzern ist.

Mein erster Vorschlag, um dieses Problem zu beheben, wäre, Ihre ViewHolders und Adapter zu entkoppeln, indem Sie sie statische innere Klassen machen. Auf diese Weise haben sie keinen Verweis auf den Adapter, so dass Ihr Kontext Feld für sie nicht zugänglich ist, und es ist auch eine gute Sache, da Kontextreferenzen sparsam übergeben und gespeichert werden sollten (auch um große Speicherlecks zu vermeiden). Wenn Sie den Kontext nur zum Abrufen der Zeichenfolgen benötigen, führen Sie ihn an einer anderen Stelle aus, z. B. im Adapterkonstruktor, aber speichern Sie den Kontext nicht als Mitglied. Schließlich scheint die DayForecastAdapter auch gefährlich: Sie übergeben die eine, gleiche Instanz davon an jede HourlyViewHolder, die wie ein Fehler scheint.

Ich denke, dass das Design Festsetzung und diese Klassen die Entkopplung sollte die Befreiung von diesem Speicher erhalten Leck

+0

Gut danke !. Ich probiere das jetzt aus! Ich vermeide es, die gleiche DayForecastAdapter-Instanz auf jedem StourlyViewHolder zu übergeben. – feresr

+0

Kein Glück, gleiche Ergebnisse ... Wenn Sie zum Zweig'forecastadapter-leak' wechseln, sehen Sie die Änderungen Ich habe mich beworben. Leider bleibt der Fehler bestehen. Danke für die Tipps, die VH statische ist eine gute Idee. – feresr

+1

Mit Blick auf Ihre Branche, ich glaube wirklich, Sie sollten pro Inhaber-Instanzen des DayForecastAdapter erstellen, und in der Zeile 157 anstelle von labelViewHolder.recyclerView.setAdapter (dayForecastAdapter) etwas wie labelViewHolder.recyclerView.adapter.setData (...) Dies sollte auch die notifyDataSetChanged() -Methode auf dem Adapter aufrufen. Da Ihr dayForecastAdapter für jeden stündlichenViewHolder einer ist, sind die viewHolder von ForecastAdapter und dayForecastAdapter auf seltsame Weise gekoppelt, vielleicht liegt das an der Wurzel des Problems? – maciekjanusz

8

konnte ich dieses Problem beheben, indem RecyclerView überschrieben. Dies geschieht, weil RecyclerView sich nie selbst von AdapterDataObservable abmeldet.

@Override protected void onDetachedFromWindow() { 
    super.onDetachedFromWindow(); 
    if (getAdapter() != null) { 
     setAdapter(null); 
    } 
} 
+0

Dieses Problem kam in einer völlig neuen Anwendung zurück, ist dies immer noch der beste Weg, um es zu lösen? – feresr

+0

@fersesr ging es nie weg :) Ich benutze immer noch das gleiche 'HackyRecyclerView' seit Android 21 – Bolein95

+0

haha ​​gut zu wissen, ich bin nicht der einzige mit diesem Problem konfrontiert. Ich mache gerade onDestroyView() {recyclerView.adapter = null}. Dies behebt auch das Problem, aber ich würde gerne wissen, was es an erster Stelle verursacht. – feresr

0

Ich kann nicht Ihr Bild öffnen und die tatsächliche Leck sehen, aber wenn Sie eine lokale Variable für Ihre RecyclerView in Ihrem Fragment definieren und Ihre Fragmenten retainInstanceStatetrue kann es möglich, Lecks mit Rotation verursachen.

Wenn ein Fragment mit retainInstance verwenden, sollten Sie alle Ihre ui Referenzen in onDestroyView

@Override 
public void onDestroyView() { 
    yourRecyclerView = null; 
    super.onDestroyView(); 
} 

löschen Hier können Sie eine detaillierte Information über diesen Link finden: Retained Fragments with UI and memory leaks

Verwandte Themen