Créer un client de navigateur multimédia

Pour terminer la conception client/serveur, vous devez créer un composant d'activité contenant le code de votre interface utilisateur, un MediaController associé et un MediaBrowser.

MediaBrowser remplit deux fonctions importantes: il se connecte à MediaBrowserService et, une fois connecté, il crée le MediaController pour votre interface utilisateur.

Remarque : L'implémentation recommandée de MediaBrowser est MediaBrowserCompat, qui est défini dans la bibliothèque Support Media-Compat. Sur cette page, le terme "MediaBrowser" fait référence à une instance de MediaBrowserCompat.

Se connecter à MediaBrowserService

Lorsque votre activité client est créée, elle se connecte à MediaBrowserService. Il y a une petite poignée de main et une petite danse. Modifiez les rappels de cycle de vie de l'activité comme suit:

  • onCreate() construit un MediaBrowserCompat. Transmettez le nom de votre MediaBrowserService et le paramètre MediaBrowserCompat.ConnectionCallback que vous avez définis.
  • onStart() se connecte à MediaBrowserService. C'est là que la magie de MediaBrowserCompat.ConnectionCallback entre en jeu. Si la connexion aboutit, le rappel onConnect() crée le contrôleur multimédia, l'associe à la session multimédia, associe vos commandes d'interface utilisateur à MediaController et enregistre le contrôleur pour recevoir les rappels de la session multimédia.
  • onResume() définit le flux audio afin que votre application réponde à la commande de volume sur l'appareil.
  • onStop() déconnecte votre MediaBrowser et annule l'enregistrement de MediaController.Callback lorsque votre activité s'arrête.

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();

  }
}

Personnaliser MediaBrowserCompat.ConnectionCallback

Lorsque votre activité construit MediaBrowserCompat, vous devez créer une instance de ConnectionCallback. Modifiez sa méthode onConnected() pour récupérer le jeton de session multimédia de MediaBrowserService, puis utilisez-le pour créer un MediaControllerCompat.

Utilisez la méthode pratique MediaControllerCompat.setMediaController() pour enregistrer un lien vers le contrôleur. Cela permet de gérer les boutons multimédias. Il vous permet également d'appeler MediaControllerCompat.getMediaController() pour récupérer le contrôleur lors de la création des commandes de transport.

L'exemple de code suivant montre comment modifier la méthode 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
    }
  };

Connecter votre UI au contrôleur multimédia

Dans l'exemple de code ConnectionCallback ci-dessus, il inclut un appel à buildTransportControls() pour étoffer votre UI. Vous devez définir des onClickListeners pour les éléments de l'interface utilisateur qui contrôlent le lecteur. Choisissez la méthode MediaControllerCompat.TransportControls appropriée pour chacune d'elles.

Votre code ressemblera à ceci, avec un onClickListener pour chaque bouton:

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);
}
}

Les méthodes TransportControls envoient des rappels à la session multimédia de votre service. Assurez-vous d'avoir défini une méthode MediaSessionCompat.Callback correspondante pour chaque commande.

Synchronisez vos données avec la session multimédia

L'UI doit afficher l'état actuel de la session multimédia, comme décrit par ses paramètres PlaybackState et Metadata. Lorsque vous créez les commandes de transport, vous pouvez récupérer l'état actuel de la session, l'afficher dans votre interface utilisateur, et activer et désactiver des commandes de transport en fonction de l'état et des actions disponibles.

Pour recevoir des rappels de la session multimédia chaque fois que son état ou ses métadonnées changent, définissez un MediaControllerCompat.Callback avec les deux méthodes suivantes:

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) {}
  };

Enregistrez le rappel lorsque vous créez les commandes de transport (voir la méthode buildTransportControls()) et annulez l'enregistrement lorsque l'activité s'arrête (dans la méthode de cycle de vie onStop() de l'activité).

Se déconnecter lorsque la session multimédia est détruite

Si la session multimédia n'est plus valide, le rappel onSessionDestroyed() est émis. Dans ce cas, la session ne peut pas redevenir fonctionnelle au cours de la durée de vie du MediaBrowserService. Bien que les fonctions liées à MediaBrowser puissent continuer à fonctionner, un utilisateur ne peut pas afficher ni contrôler la lecture d'une session multimédia détruite, ce qui réduira probablement la valeur de votre application.

Par conséquent, lorsque la session est détruite, vous devez vous déconnecter de MediaBrowserService en appelant disconnect(). Cela garantit que le service de navigateur n'est associé à aucun client et peut être détruit par le système d'exploitation. Si vous devez vous reconnecter ultérieurement au MediaBrowserService (par exemple, si votre application souhaite maintenir une connexion persistante à l'application multimédia), créez une nouvelle instance de MediaBrowser au lieu de réutiliser l'ancienne.

L'extrait de code suivant illustre une implémentation de rappel qui se déconnecte du service du navigateur lorsque la session multimédia est détruite:

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
    }
  };