Welche Android-Tools und -Methoden eignen sich am besten, um Speicher- / Ressourcenlecks zu finden? [geschlossen]

152

Ich habe eine Android-App entwickelt und bin am Punkt einer Telefon-App-Entwicklung angelangt, bei der alles gut zu funktionieren scheint und Sie den Sieg erklären und versenden möchten, aber Sie wissen, dass es nur einige Speicher- und Ressourcenlecks geben muss da drin; und es gibt nur 16 MB Heap auf dem Android und es ist anscheinend überraschend einfach, in einer Android-App zu lecken.

Ich habe mich umgesehen und konnte bisher nur Informationen zu 'hprof' und 'traceview' ausgraben und bekomme auch nicht viele positive Bewertungen.

Welche Tools oder Methoden sind Ihnen begegnet oder wurden entwickelt und möchten Sie möglicherweise in einem Betriebssystemprojekt teilen?

Jottos
quelle
3
Kann ich dafür stimmen, dass name seinen Namen in etwas lesbares ändert?
JPM
1
Wäre es in Ordnung, diese Frage erneut zu öffnen, wenn wir das Wort "Android Tools" aus dem Fragentitel entfernen? Die hier gefundenen Antworten waren sehr nützlich, um meine Probleme mit Speicherverlusten zu lösen
k3b
Okay, ich habe einen Benutzer, der ständig zwischen Aktivitäten wechselt, was bedeutet, dass er in 20 Sekunden 15 Aktivitäten gewechselt hat. Könnte dies eine Ursache für einen Speicherfehler sein? Was soll ich tun, um das Problem zu beheben? Vielen Dank!
Ruchir Baronia
1
Ich kann dies nicht als Antwort geben, da die Frage geschlossen wurde. Ich würde jedoch empfehlen, einen Blick auf Leak Canary zu werfen . Verwenden Sie einfach Ihre App, öffnen und schließen Sie Aktivitäten und lassen Sie die Bibliothek ihre Arbeit erledigen. Es wird Ihnen sogar sagen, wo das Leck aufgetreten ist. Geben Sie dem Leckanalysator nach dem Auftreten des Lecks etwas Zeit, um seine Arbeit zu erledigen. Normalerweise dauert es mindestens 2 Minuten, bis die Quelle des Lecks gefunden wurde. Danach wird es Ihnen ordentlich in der App präsentiert. Keine zusätzlichen Werkzeuge erforderlich!
Ubuntudroid

Antworten:

90

Einer der häufigsten Fehler bei der Entwicklung von Android-Apps ist der Fehler "java.lang.OutOfMemoryError: Bitmap-Größe überschreitet VM-Budget". Ich habe diesen Fehler häufig bei Aktivitäten festgestellt, bei denen viele Bitmaps verwendet wurden, nachdem die Ausrichtung geändert wurde: Die Aktivität wird zerstört, erneut erstellt und die Layouts werden aus dem XML-Code "aufgeblasen", wodurch der für Bitmaps verfügbare VM-Speicher belegt wird.

Bitmaps im vorherigen Aktivitätslayout werden vom Garbage Collector nicht ordnungsgemäß freigegeben, da sie Verweise auf ihre Aktivität gekreuzt haben. Nach vielen Experimenten habe ich eine recht gute Lösung für dieses Problem gefunden.

Legen Sie zunächst das Attribut "id" in der übergeordneten Ansicht Ihres XML-Layouts fest:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Rufen Sie dann in der onDestroy () -Methode Ihrer Aktivität die unbindDrawables () -Methode auf, die eine Referenz an die übergeordnete Ansicht übergibt, und führen Sie dann ein System.gc () aus.

@Override
protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
}


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

Diese unbindDrawables () -Methode untersucht den Ansichtsbaum rekursiv und:

  1. Entfernt Rückrufe auf allen Hintergrundzeichnungen
  2. Entfernt untergeordnete Elemente in jeder Ansichtsgruppe
hp.android
quelle
3
Gute Lösung für ein häufiges Problem.
While-E
9
Dies funktioniert nicht für Unterklassen von AdapterView (ListView, GridView usw.).
Arjun
@Arjun Ja..es funktioniert nicht für AdapterView-Unterklassen. Dafür müssen Sie Ausnahmen behandeln. Ruhe es funktioniert gut. Das ist es, was ich in meinem Code verwende und es funktioniert gut. Hoffe das hilft.
HP.android
@ hp.android Mit "Behandle dies in der Ausnahme" hast du ein Beispiel, wie das aussehen würde? Ich frage, weil ich Seiten mit Bildern in meinem habe PageAdapterund ich von diesem Fehler bearbeitet werde :(
Jacksonkr
4
@ Jackson ändern Sie einfach die Bedingung: if (Instanz von ViewGroup anzeigen &&! (Instanz von AdapterView anzeigen)) wird die Ausnahme
beseitigen,
28

Hauptsächlich für Google-Reisende aus der Zukunft:

Die meisten Java-Tools sind für diese Aufgabe leider nicht geeignet, da sie nur den JVM-Heap analysieren. Jede Android-Anwendung hat jedoch auch einen nativen Heap, der ebenfalls innerhalb der ~ 16 MB-Grenze liegen muss. Es wird normalerweise zum Beispiel für Bitmap-Daten verwendet. Sie können also leicht auf Speicherfehler stoßen, obwohl Ihr JVM-Heap etwa 3 MB groß ist, wenn Sie viele Drawables verwenden.

Timo Ohr
quelle
6
ab Android 3.0 (Waben) Drawables werden auf dem Heap gespeichert
Gu1234
@ Timo Was würden Sie dann verwenden, um Lecks im nativen Heap zu erkennen?
Sydney
1
Testen, viele Tests. Das Problem ist, dass Sie aufgrund der gemeinsamen Nutzung des Speichers und anderer Optimierungstechniken nicht einmal wirklich feststellen können, wie viel Speicher Ihre App verwendet. Sie können Speicherwerte mit den üblichen Shell-Befehlen abrufen, dies sind jedoch sehr, sehr grobe Schätzungen.
Timo Ohr
20

Die Antwort von @ hp.android funktioniert gut, wenn Sie nur mit Bitmap-Hintergründen arbeiten, aber in meinem Fall hatte ich BaseAdaptereine Reihe von ImageViews für a GridView. Ich habe die unbindDrawables()Methode wie empfohlen geändert , sodass folgende Bedingung erfüllt ist:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

aber das Problem ist dann, dass die rekursive Methode niemals die Kinder der AdapterView. Um dies zu beheben, habe ich stattdessen Folgendes getan:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

Damit die untergeordneten AdapterViewElemente weiterhin verarbeitet werden, versucht die Methode nicht, alle untergeordneten Elemente zu entfernen (was nicht unterstützt wird).

Dies behebt das Problem jedoch nicht ganz, da ImageViews eine Bitmap verwaltet, die nicht ihr Hintergrund ist. Ich habe daher folgendes hinzugefügt. Es ist nicht ideal, aber es funktioniert:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

Insgesamt ist die unbindDrawables()Methode dann:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

Ich hoffe, dass es einen prinzipielleren Ansatz gibt, um solche Ressourcen freizusetzen.

Darrenp
quelle
12

Gutes Google I / O-Gespräch (2011) über Speicherverwaltung in Android sowie Details zu Tools und Techniken für die Speicherprofilerstellung:
http://www.youtube.com/watch?v=_CruQY55HOk

greg7gkb
quelle
4
Oder der entsprechende Blog-Beitrag: android-developers.blogspot.com/2011/03/…
greg7gkb
1
Okay, ich habe einen Benutzer, der ständig zwischen Aktivitäten wechselt, was bedeutet, dass er in 20 Sekunden 15 Aktivitäten gewechselt hat. Könnte dies eine Ursache für einen Speicherfehler sein? Was soll ich tun, um das Problem zu beheben? Vielen Dank!'
Ruchir Baronia
1

Nun, das sind die Tools, die sich mit den einzigartigen Formaten verbinden, die Android verwendet. Ich denke, Sie sind möglicherweise nicht zufrieden mit dem zugrunde liegenden Testcode-Framework, das verwendet wird.

Haben Sie versucht, Codebereiche mit dem Android Mock Framework zu testen?

Fred Grott
quelle
1
Nicht so sehr, Tests dieser Art sind weniger das Problem als vielmehr das Aufzeichnen, was tatsächlich passiert, während die Anwendung ausgeführt wird. Was ich wirklich brauche, ist ein Tool zur
Erstellung von