2017-07-20 2 views
2

[siehe Update unten]MediaPlayer-Instanzen bleiben nach dem Freigeben im Heap

Ich habe eine Taste, die nach dem Drücken einen kurzen Ton abspielt. Der Sound-Player-Klasse sieht wie folgt aus:

public void play (String filePath) { 
    ... 
    AssetFileDescriptor afd = assetManager.openFd(filePath) 
    play(afd) 
} 

private void play(AssetFileDescriptor afd) { 
     try { 
      MediaPlayer mVoicePlayer = new MediaPlayer(); 
      mVoicePlayer.setOnCompletionListener(this); 
      mVoicePlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 
      afd.close(); 
      mVoicePlayer.prepare(); 
      mVoicePlayer.setLooping(false); 
      mVoicePlayer.start(); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
    } 

@Override 
    public void onCompletion(MediaPlayer mp) { 
     mp.release(); 
     mp = null; 
     Log.v (TAG, "media released!"); 
    } 

Die Idee ist, einen Ton zu spielen und lassen Sie die MediaPlayer und frei seinen Speicher, wenn es nicht mehr verwendet werden (der Sound hat sich Spiel abgeschlossen).

aber nach dem Ton 5mal und Blick auf der Heapdump in Android Studio-Monitor spielt die Totalcount Spalte der MediaPlayer Klasse ist 5. Auch nach Drücken der Taste im Monitor „GC Initiate“ und dann machen der Heap-Dump erneut, es ist immer noch 5. Spielen noch einmal macht die Zählung 6. Zum Vergleich ist der TotalCount von AssetFileDescriptor Klasse 5 vor GC und 0 nach GC.

Die ReferenceTree für die 5 MediaPlayer Instanzen (nach GC) ähnlich aussehen wie diese (Adressen unterscheiden):

enter image description here

ich auch eine Änderung des Codes versucht, wo die setOnCompletionListener() einen neuen Listener inline erstellt statt Bezug auf this, aber das Ergebnis war das gleiche.

Warum diese Instanzen im Speicher bleiben und wie man damit umgeht?

[Update]

Ich habe noch ein paar Tests gemacht. Zunächst einmal sieht es so aus, als ob der GC-Knopf in Android Studio Monitor die nicht verwendeten MediaPlayer-Referenzen sammelt, aber nur beim zweiten Durchlauf. Selbst wenn 300 ungenutzte Instanzen vorhanden sind (der Sound wurde 300 Mal abgespielt), werden sie beim ersten Klick auf den GC-Button nicht gelöscht, nur beim zweiten Klick. Dieser Effekt war wiederholbar.

In einem zweiten Test habe ich den Ton so oft abgespielt, dass ein automatischer GC auf dem Telefon aufgetreten ist (ohne auf den GC-Knopf zu klicken). Es war in der blauen Grafik des Android Studio-Speichermonitors sichtbar: Nachdem der freie Speicher den Wert Null erreicht hatte, wurde der freie Speicher um einige MB erhöht, und gleichzeitig wurde der zugewiesene Speicher gelöscht (ein Zeichen des automatischen GC). Die MediaPlayer-Instanzen wurden jedoch nicht veröffentlicht, obwohl es bereits 700+ in Heap Dump gab.

In einem dritten Test spielte ich den Sound noch ein paar hundert Mal. Irgendwann bemerkte ich, dass ihre Anzahl auf dem Heap-Dump abnahm (aber auf einige Hundert statt auf Null). So

,

vielleicht ist das kein Problem mit meinem Code und dies so ausgelegt ist, funktioniert die automatische GC selbst entscheidet, ob und wie viele Instanzen zu sammeln und klar. Und die GC-Schaltfläche in Android Studio sammelt aus irgendeinem Grund nicht alles (zumindest beim ersten Start).

Antwort

0

Bitte versuchen Sie dies, indem Sie die Instanzvariable des MediaPlayers wie unten verwenden.

MediaPlayer mVoicePlayer; 
//-------------------- 
public void play (String filePath) { 

    AssetFileDescriptor afd = assetManager.openFd(filePath) 
    play(afd); 
} 

private void play(AssetFileDescriptor afd) { 
    try { 
     mVoicePlayer = new MediaPlayer(); 
     mVoicePlayer.setOnCompletionListener(this); 
     mVoicePlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 
     afd.close(); 
     mVoicePlayer.prepare(); 
     mVoicePlayer.setLooping(false); 
     mVoicePlayer.start(); 
    } catch (IOException e) { 
     throw new RuntimeException(e); 
    } 
} 

@Override 
public void onCompletion(MediaPlayer mp) { 
    if (mVoicePlayer!=null){ 
     mVoicePlayer.release();  
    } 

    Log.v (TAG, "media released!"); 
} 
+0

Ich versuchte Ihre Lösung, aber das Ergebnis war das gleiche; Siehe mein Update. – camcam

+0

Sind Sie sicher, dass die play() -Methode in keiner Situation mehrere Male aufgerufen wird? Wenn dies passiert, dann gibt es Fälle, in denen mehr als ein Objekt von Ihrem Media Player erstellt wurde. –

Verwandte Themen