android - tiradero - Causas de la recolección de basura: MediaPlayer finalizado sin ser lanzado
servicio de recolección de basura (2)
Creo que esto se debe a que creas el reproductor multimedia dentro del alcance del método, por lo tanto, cuando el método se completa, queda fuera del alcance. Esto significa que no hay referencias, por lo que está bien para la recolección de basura.
Esto significa que puede ser liberado por el GC antes de que incluso haya llamado onCompletion, por lo tanto, no se liberará antes de ser borrado. En su lugar, debe almacenar una referencia al reproductor multimedia como una variable miembro en su clase.
¡Después de mucha depuración finalmente encontré lo que está causando este error! ¡Recolección de basura!
Tengo un video reproducido en vista de medios y en el fondo, estoy buscando nuevos videos de una API Rest.
De vez en cuando veo la recolección de basura en ejecución:
02-22 13:14:57.969: D/dalvikvm(16888): GC_EXPLICIT freed 152K, 4% free 6746K/6979K, paused 2ms+2ms
Y la recta después de eso:
02-22 13:14:57.969: W/MediaPlayer-JNI(16888): MediaPlayer finalized without being released
Así que lo probé llamando a System.gc () cada 5 segundos.
¡Tan pronto como se llama el primer GC sucede!
02-22 13:19:47.813: D/dalvikvm(17060): GC_EXPLICIT freed 167K, 5% free 6745K/7047K, paused 2ms+2ms ---- I call GC
02-22 13:19:47.813: W/MediaPlayer-JNI(17060): MediaPlayer finalized without being released ---- VIDEO PLAY INTERRUPTED
¿Por qué pasó esto? ¿Puedo prevenirlo?
Reproduciendo el video:
private void playMedia(int playListIndex) throws IOException {
File mediadir = getDir("tvr", Context.MODE_PRIVATE);
filelist = mediadir.listFiles();
Log.i("media player", "play media!");
String path = filelist[playListIndex].getAbsolutePath();
FileInputStream fileInputStream = new FileInputStream(path);
final Uri uri = Uri.parse(path);
String filename = filelist[playListIndex].getName();
if (filename.contains("image")) {
imageView = (ImageView)findViewById(R.id.imageView);
imageView.setVisibility(View.VISIBLE);
imageView.setImageURI(uri);
mHandler.postDelayed(new Runnable() {
public void run() {
imageView.setVisibility(View.GONE);
imageView.setImageURI(uri);
onCompletion(null);
}
}, 4000);
} else if (filename.contains("video")) {
MediaPlayer pl = new MediaPlayer();
pl.setOnCompletionListener(this);
pl.setDisplay(holder);
pl.setDataSource(fileInputStream.getFD());
pl.prepare();
pl.start();
}
}
Y cuando esté hecho:
@Override
public void onCompletion(MediaPlayer mp) {
Log.i("media player", "play next please!");
if (mp != null) {
mp.release();
}
// play next video
currentMedia++;
if (currentMedia > playList.size() - 1) {
currentMedia = 0;
}
try {
playMedia(currentMedia);
} catch (IOException e) {
e.printStackTrace();
}
}
Java GC solo gestiona la memoria. Por lo tanto, cuando se utilizan otros recursos (por ejemplo, archivos, sockets, etc.), debe administrarlos manualmente. En el caso de MediaPlayer, la documentation menciona que:
También se recomienda que una vez que el objeto MediaPlayer ya no se esté utilizando, llame a release () inmediatamente para que los recursos utilizados por el motor del reproductor interno asociado con el objeto MediaPlayer se puedan liberar inmediatamente. Los recursos pueden incluir recursos singleton, como los componentes de aceleración de hardware y la falla de llamar a release () puede hacer que las instancias subsiguientes de los objetos de MediaPlayer se repliquen en las implementaciones de software o falle por completo.
Entonces, cuando haya terminado con una instancia de MediaPlayer, debe asegurarse de llamar explícitamente a release () en esa instancia. Un buen lugar para hacer esto podría ser un método de ciclo de vida de la Actividad contenedora, por ejemplo, onDestroy (). De lo contrario, cuando la instancia de MediaPlayer finalmente se recoja como basura (en algún momento arbitrario después de que ya no haga referencia), el finalizador notará que nunca llamó a release () y emitirá la advertencia que está viendo.