Creazione di un client browser multimediale

Per completare la progettazione client/server, devi creare un componente di attività che contenga il tuo codice UI, un MediaController associato e un MediaBrowser.

MediaBrowser svolge due importanti funzioni: si connette a MediaBrowserService e, al momento della connessione, crea il MediaController per l'interfaccia utente.

Nota: l'implementazione consigliata di MediaBrowser è MediaBrowserCompat, definita nella libreria di supporto di Media-Compat. In questa pagina il termine "MediaBrowser" si riferisce a un'istanza di MediaBrowserCompat.

Connettiti a MediaBrowserService

Una volta creata, l'attività client si connette a MediaBrowserService. Ci sono una piccola stretta di mano e una danza. Modifica i callback del ciclo di vita dell'attività come segue:

  • onCreate() crea un MediaBrowserCompat. Inserisci il nome del tuo MediaBrowserService e MediaBrowserCompat.ConnectionCallback che hai definito.
  • onStart() si connette a MediaBrowserService. Ecco dove entra in gioco MediaBrowserCompat.ConnectionCallback. Se la connessione viene stabilita, il callback onConnect() crea il controller multimediale, lo collega alla sessione multimediale, collega i controlli UI a MediaController e registra il controller per ricevere callback dalla sessione multimediale.
  • onResume() imposta lo stream audio in modo che la tua app risponda al controllo del volume sul dispositivo.
  • onStop() disconnette il tuo MediaBrowser e annulla la registrazione di MediaController.Callback quando l'attività si interrompe.

Kotlin

class MediaPlayerActivity : AppCompatActivity() {

    private lateinit var mediaBrowser: MediaBrowserCompat

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        // Create MediaBrowserServiceCompat
        mediaBrowser = MediaBrowserCompat(
                this,
                ComponentName(this, MediaPlaybackService::class.java),
                connectionCallbacks,
                null // optional Bundle
        )
    }

    public override fun onStart() {
        super.onStart()
        mediaBrowser.connect()
    }

    public override fun onResume() {
        super.onResume()
        volumeControlStream = AudioManager.STREAM_MUSIC
    }

    public override fun onStop() {
        super.onStop()
        // (see "stay in sync with the MediaSession")
        MediaControllerCompat.getMediaController(this)?.unregisterCallback(controllerCallback)
        mediaBrowser.disconnect()
    }
}

Java

public class MediaPlayerActivity extends AppCompatActivity {
  private MediaBrowserCompat mediaBrowser;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    // Create MediaBrowserServiceCompat
    mediaBrowser = new MediaBrowserCompat(this,
      new ComponentName(this, MediaPlaybackService.class),
        connectionCallbacks,
        null); // optional Bundle
  }

  @Override
  public void onStart() {
    super.onStart();
    mediaBrowser.connect();
  }

  @Override
  public void onResume() {
    super.onResume();
    setVolumeControlStream(AudioManager.STREAM_MUSIC);
  }

  @Override
  public void onStop() {
    super.onStop();
    // (see "stay in sync with the MediaSession")
    if (MediaControllerCompat.getMediaController(MediaPlayerActivity.this) != null) {
      MediaControllerCompat.getMediaController(MediaPlayerActivity.this).unregisterCallback(controllerCallback);
    }
    mediaBrowser.disconnect();

  }
}

Personalizza MediaBrowserCompat.ConnectionCallback

Quando l'attività crea MediaBrowserCompat, devi creare un'istanza di ConnectionCallback. Modifica il relativo metodo onConnected() per recuperare il token della sessione multimediale da MediaBrowserService e utilizza il token per creare un MediaControllerCompat.

Utilizza il metodo pratico MediaControllerCompat.setMediaController() per salvare un link al controller. Consente di gestire i pulsanti multimediali. Consente inoltre di chiamare MediaControllerCompat.getMediaController() per recuperare il controller durante la creazione dei controlli di trasporto.

Il seguente esempio di codice mostra come modificare il metodo onConnected().

Kotlin

private val connectionCallbacks = object : MediaBrowserCompat.ConnectionCallback() {
    override fun onConnected() {

        // Get the token for the MediaSession
        mediaBrowser.sessionToken.also { token ->

            // Create a MediaControllerCompat
            val mediaController = MediaControllerCompat(
                    this@MediaPlayerActivity, // Context
                    token
            )

            // Save the controller
            MediaControllerCompat.setMediaController(this@MediaPlayerActivity, mediaController)
        }

        // Finish building the UI
        buildTransportControls()
    }

    override fun onConnectionSuspended() {
        // The Service has crashed. Disable transport controls until it automatically reconnects
    }

    override fun onConnectionFailed() {
        // The Service has refused our connection
    }
}

Java

private final MediaBrowserCompat.ConnectionCallback connectionCallbacks =
  new MediaBrowserCompat.ConnectionCallback() {
    @Override
    public void onConnected() {

      // Get the token for the MediaSession
      MediaSessionCompat.Token token = mediaBrowser.getSessionToken();

      // Create a MediaControllerCompat
      MediaControllerCompat mediaController =
        new MediaControllerCompat(MediaPlayerActivity.this, // Context
        token);

      // Save the controller
      MediaControllerCompat.setMediaController(MediaPlayerActivity.this, mediaController);

      // Finish building the UI
      buildTransportControls();
    }

    @Override
    public void onConnectionSuspended() {
      // The Service has crashed. Disable transport controls until it automatically reconnects
    }

    @Override
    public void onConnectionFailed() {
      // The Service has refused our connection
    }
  };

Collega la tua UI al controller multimediale

Nel codice di esempio ConnectionCallback riportato sopra, include una chiamata a buildTransportControls() per arricchire la tua UI. Occorre impostare i IstioListener per gli elementi dell'interfaccia utente che controllano il player. Scegli il metodo MediaControllerCompat.TransportControls appropriato per ciascuno.

Il codice avrà un aspetto simile a questo, con un IstioListener per ogni pulsante:

Kotlin

fun buildTransportControls() {
    val mediaController = MediaControllerCompat.getMediaController(this@MediaPlayerActivity)
    // Grab the view for the play/pause button
    playPause = findViewById<ImageView>(R.id.play_pause).apply {
        setOnClickListener {
            // Since this is a play/pause button, you'll need to test the current state
            // and choose the action accordingly

            val pbState = mediaController.playbackState.state
            if (pbState == PlaybackStateCompat.STATE_PLAYING) {
                mediaController.transportControls.pause()
            } else {
                mediaController.transportControls.play()
            }
        }
    }

    // Display the initial state
    val metadata = mediaController.metadata
    val pbState = mediaController.playbackState

    // Register a Callback to stay in sync
    mediaController.registerCallback(controllerCallback)
}

Java

void buildTransportControls()
{
  // Grab the view for the play/pause button
  playPause = (ImageView) findViewById(R.id.play_pause);

  // Attach a listener to the button
  playPause.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      // Since this is a play/pause button, you'll need to test the current state
      // and choose the action accordingly

      int pbState = MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getPlaybackState().getState();
      if (pbState == PlaybackStateCompat.STATE_PLAYING) {
        MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().pause();
      } else {
        MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().play();
      }
  });

  MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(MediaPlayerActivity.this);

  // Display the initial state
  MediaMetadataCompat metadata = mediaController.getMetadata();
  PlaybackStateCompat pbState = mediaController.getPlaybackState();

  // Register a Callback to stay in sync
  mediaController.registerCallback(controllerCallback);
}
}

I metodi TransportControls inviano callback alla sessione multimediale del tuo servizio. Assicurati di aver definito un metodo MediaSessionCompat.Callback corrispondente per ogni controllo.

Sincronizzati con la sessione multimediale

L'interfaccia utente dovrebbe visualizzare lo stato corrente della sessione multimediale, come descritto da PlaybackState e Metadata. Quando crei i controlli di trasporto, puoi acquisire lo stato attuale della sessione, visualizzarlo nella tua UI e attivare e disattivare i controlli di trasporto in base allo stato e alle azioni disponibili.

Per ricevere callback dalla sessione multimediale ogni volta che lo stato o i metadati cambiano, definisci un elemento MediaControllerCompat.Callback con questi due metodi:

Kotlin

private var controllerCallback = object : MediaControllerCompat.Callback() {

    override fun onMetadataChanged(metadata: MediaMetadataCompat?) {}

    override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {}
}

Java

MediaControllerCompat.Callback controllerCallback =
  new MediaControllerCompat.Callback() {
    @Override
    public void onMetadataChanged(MediaMetadataCompat metadata) {}

    @Override
    public void onPlaybackStateChanged(PlaybackStateCompat state) {}
  };

Registra il callback quando crei i controlli di trasporto (vedi il metodo buildTransportControls()) e annulla la registrazione all'interruzione dell'attività (nel metodo del ciclo di vita onStop() dell'attività).

Disconnettiti quando la sessione multimediale viene eliminata

Se la sessione multimediale non è valida, viene eseguito il callback onSessionDestroyed(). In questo caso, la sessione non può tornare operativa entro il periodo di validità di MediaBrowserService. Anche se le funzioni relative a MediaBrowser potrebbero continuare a funzionare, un utente non può visualizzare o controllare la riproduzione da una sessione multimediale eliminata, il che probabilmente diminuirà il valore della tua applicazione.

Pertanto, quando la sessione viene eliminata, devi disconnetterti da MediaBrowserService chiamando disconnect(). Ciò garantisce che il servizio browser non abbia client associati e può essere eliminato dal sistema operativo. Se devi riconnetterti a MediaBrowserService in un secondo momento (ad esempio, se la tua applicazione vuole mantenere una connessione permanente all'app multimediale), crea una nuova istanza di MediaBrowser anziché riutilizzare quella precedente.

Il seguente snippet di codice mostra un'implementazione di callback che si disconnette dal servizio browser quando la sessione multimediale viene eliminata:

Kotlin

private var controllerCallback = object : MediaControllerCompat.Callback() {
    override fun onSessionDestroyed() {
      mediaBrowser.disconnect()
      // maybe schedule a reconnection using a new MediaBrowser instance
    }
}

Java

MediaControllerCompat.Callback controllerCallback =
  new MediaControllerCompat.Callback() {
    @Override
    public void onSessionDestroyed() {
      mediaBrowser.disconnect();
      // maybe schedule a reconnection using a new MediaBrowser instance
    }
  };