Actualización del contenido de subcripción de MediaBrowserService

TL; DR: He creado y acoplado (mediante una suscripción) una actividad a un service de browser de medios. Este service de browser de medios puede continuar funcionando y reproducir música en segundo plano. Me gustaría poder actualizar el contenido en algún momento, ya sea cuando la aplicación vuelva a estar en primer plano o durante un evento SwipeRefreshLayout.

Tengo la siguiente funcionalidad que me gustaría implementar:

  1. Inicie un service MediaBrowserServiceCompat.
  2. Desde una actividad, conéctese y suscríbase al service de browser multimedia.
  3. Permita que el service continúe ejecutándose y reproduciendo música mientras la aplicación está cerrada.
  4. En una etapa posterior, o en un evento SwipeRefreshLayout, reconéctese y suscríbase al service para get contenido nuevo.

El problema que recibo es que dentro de un MediaBrowserService (después de que se ha creado una suscripción) solo se puede llamar a sendResult () una vez desde el método onLoadChildren (), así que la próxima vez que intentes suscribirte al service de browser de medios usando la misma raíz , obtiene la siguiente exception cuando sendResult () se invoca por segunda vez:

E/UncaughtException: java.lang.IllegalStateException: sendResult() called when either sendResult() or sendError() had already been called for: MEDIA_ID_ROOT at android.support.v4.media.MediaBrowserServiceCompat$Result.sendResult(MediaBrowserServiceCompat.java:602) at com.roostermornings.android.service.MediaService.loadChildrenImpl(MediaService.kt:422) at com.roostermornings.android.service.MediaService.access$loadChildrenImpl(MediaService.kt:50) at com.roostermornings.android.service.MediaService$onLoadChildren$1$onSyncFinished$playerEventListener$1.onPlayerStateChanged(MediaService.kt:376) at com.google.android.exoplayer2.ExoPlayerImpl.handleEvent(ExoPlayerImpl.java:422) at com.google.android.exoplayer2.ExoPlayerImpl$1.handleMessage(ExoPlayerImpl.java:103) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:150) at android.app.ActivityThread.main(ActivityThread.java:5665) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:822) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:712) 

Llamo a los siguientes methods para conectarme y desconectarme del browser de medios (nuevamente, todo funciona sin problemas en la primera connection, pero en la segunda connection no estoy seguro de cómo actualizar el contenido a través de una suscripción):

 override fun onStart() { super.onStart() mMediaBrowser = MediaBrowserCompat(this, ComponentName(this, MediaService::class.java), connectionCallback, null) if (!mMediaBrowser.isConnected) mMediaBrowser.connect() } override fun onPause() { super.onPause() //Unsubscribe and unregister MediaControllerCompat callbacks MediaControllerCompat.getMediaController(this@DiscoverFragmentActivity)?.unregisterCallback(mediaControllerCallback) if (mMediaBrowser.isConnected) { mMediaBrowser.unsubscribe(mMediaBrowser.root, subscriptionCallback) mMediaBrowser.disconnect() } } 

Me desconecto y desconecto en onPause () en lugar de onDestroy () para que se vuelva a crear la suscripción, incluso si la actividad se mantiene en la stack de respaldo.

Otra investigación:

Me he referido al código de Google Samples en Github, así como …

  • https://github.com/googlesamples/android-MediaBrowserService
  • https://github.com/moondroid/UniversalMusicPlayer

Ninguno de los repos anteriores parece manejar el problema del contenido refrescante después de que se haya creado el service de browser de medios y la actividad se haya suscrito al less una vez. Me gustaría evitar reiniciar el service para que la música pueda seguir reproduciéndose en segundo plano.

Posibles problemas relacionados:

  • MediaBrowser.subscribe no funciona después de que regrese a la actividad 1 de la actividad 2 (6.0.1 Android) –no efecto en el problema actual

Mi problema no estaba relacionado con la class MediaBrowserServiceCompat. El problema result.detach() porque estaba llamando a result.detach() para implementar una recuperación de datos asincrónica, y el oyente que estaba usando tenía las variables parentId y de result del método onLoadChildren pasado y se le asignó el valor final en lugar de var .

Todavía no entiendo completamente por qué ocurre esto, si es un resultado subyacente de usar un Player.EventListener dentro de otro oyente de llamada de networking asincrónica, pero la solución fue crear y asignar una variable (y tal vez alguien más pueda explicar este fenómeno):

 // Create variable var currentResult: Result<List<MediaBrowserCompat.MediaItem>>? = null override fun onLoadChildren(parentId: String, result: MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>>) { // Use result.detach to allow calling result.sendResult from another thread result.detach() // Assign returned result to temporary variable currentResult = result currentParentId = parentId // Create listener for network call ChannelManager.onFlagChannelManagerDataListener = object : ChannelManager.Companion.OnFlagChannelManagerDataListener { override fun onSyncFinished() { // Create a listener to determine when player is prepanetworking val playerEventListener = object : Player.EventListener { override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) { when(playbackState) { Player.STATE_READY -> { if(mPlayerPreparing) { // Prepare content to send to subscribed content loadChildrenImpl(currentParentId, currentResult as MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>>) mPlayerPreparing = false } } ... } } } }