Garbage Collection causa: MediaPlayer finalizado sin ser liberado

Después de una gran cantidad de depuración, finalmente encontré lo que está causando este error. ¡Recolección de basura!

Tengo un video reproduciéndose en la vista de medios y en el fondo estoy buscando videos nuevos de una API de descanso.

De vez en cuando veo que se está ejecutando la recolección de basura:

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 llame al primer GC, sucederá!

 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?

Reproducción del 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(); } } 

Creo que esto se debe a que crea el reproductor de medios dentro del scope del método, por lo tanto, cuando el método finaliza, sale del scope. 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 haya llamado a Completar, por lo tanto no se liberará antes de borrado. En su lugar, debe almacenar una referencia al reproductor multimedia como una variable miembro en su clase.

Java GC solo administra la memoria. Por lo tanto, cuando se utilizan otros recursos (por ejemplo, archivos, conectores, etc.), debe administrarlos manualmente. En el caso de MediaPlayer, la documentación menciona que:

También se recomienda que una vez que un objeto MediaPlayer ya no se utilice, llame a release () inmediatamente para que los recursos utilizados por el motor de reproducción interna asociado con el objeto MediaPlayer puedan liberarse inmediatamente. El recurso puede incluir recursos únicos como los componentes de aceleración de hardware y la falla al invocar a la versión () puede ocasionar que las instancias posteriores de los objetos MediaPlayer retrocedan a las implementaciones de software o fallen 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 que lo contiene, por ejemplo, onDestroy (). De lo contrario, cuando la instancia de MediaPlayer sea finalmente recolectada como basura (en algún momento arbitrario después de que ya no lo referencia), el finalizador notará que nunca llamó a release () y emitirá la advertencia que está viendo.