Aby zakończyć projektowanie klienta/serwera, musisz utworzyć komponent aktywności zawierający kod interfejsu użytkownika, powiązany kontroler MediaController i MediaBrowser.
MediaBrowser spełnia 2 ważne funkcje: łączy się z usługą MediaBrowserService, a po połączeniu tworzy element MediaController dla Twojego interfejsu.
Uwaga: zalecaną implementacją MediaBrowser jest MediaBrowserCompat
. Jest ona zdefiniowana w bibliotece pomocy Media-Compat.
Na tej stronie termin „MediaBrowser” odnosi się do wystąpienia obiektu MediaBrowserCompat.
Łączenie z MediaBrowserService
Tworzona aktywność klienta powoduje nawiązanie połączenia z MediaBrowserService. Trzeba tylko uzgadniać rękę i tańczyć. Zmodyfikuj wywołania zwrotne w cyklu życia aktywności w ten sposób:
onCreate()
tworzy obiekt MediaBrowserCompat. Wpisz nazwę usługi MediaBrowserService i zdefiniowanej przez siebie klasy MediaBrowserCompat.ConnectionCallback.onStart()
łączy się z MediaBrowserService. Oto magia MediaBrowserCompat.ConnectionCallback. Jeśli nastąpi połączenie, wywołanie zwrotne onConnect() tworzy kontroler mediów, łączy go z sesją multimediów, łączy elementy interfejsu z elementami MediaController oraz rejestruje, że kontroler otrzymuje wywołania zwrotne z sesji multimediów.onResume()
ustawia strumień audio w taki sposób, aby aplikacja reagowała na sterowanie głośnością na urządzeniu.onStop()
odłącza MediaBrowser i wyrejestruje element MediaController.Callback, gdy aktywność się zakończy.
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(); } }
Dostosuj MediaBrowserCompat.ConnectionCallback
Gdy działanie tworzy element MediaBrowserCompat, musisz utworzyć instancję ConnectionCallback. Zmodyfikuj jej metodę onConnected()
, aby pobrać token sesji multimediów z MediaBrowserService i użyj tego tokena do utworzenia klasy MediaControllerCompat.
Skorzystaj z wygodnej metodyMediaControllerCompat.setMediaController()
, aby zapisać połączenie z kontrolerem. Umożliwia to obsługę przycisków multimediów. Umożliwia też wywołanie MediaControllerCompat.getMediaController()
w celu pobrania kontrolera podczas tworzenia elementów sterujących transportu.
Poniższy przykładowy kod pokazuje, jak zmodyfikować metodę 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 } };
Połącz interfejs użytkownika z kontrolerem multimediów
W powyższym przykładowym kodzie ConnectionCallback znajduje się wywołanie buildTransportControls()
w celu dopracowania interfejsu. Konieczne jest ustawienie parametru onClickListeners dla elementów interfejsu użytkownika, które sterują odtwarzaczem. Wybierz odpowiednią metodę MediaControllerCompat.TransportControls
w przypadku każdej z nich.
Twój kod będzie wyglądać mniej więcej tak, ale każdy przycisk będzie mieć parametr onClickListener:
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); } }
Metody TransportControls wysyłają wywołania zwrotne do sesji multimediów Twojej usługi. Upewnij się, że dla każdego elementu sterującego została zdefiniowana odpowiadająca jej metoda MediaSessionCompat.Callback
.
Synchronizuj swoje dane z sesją multimediów
Interfejs powinien wyświetlać bieżący stan sesji multimediów, zgodnie z parametrami PlaybackState i Metadata. Po utworzeniu elementów sterujących transportu możesz przechwycić bieżący stan sesji, wyświetlić go w interfejsie użytkownika oraz włączyć lub wyłączyć te ustawienia w zależności od stanu i dostępnych działań.
Aby otrzymywać wywołania zwrotne z sesji multimedialnych za każdym razem, gdy zmieni się jej stan lub metadane, zdefiniuj parametr MediaControllerCompat.Callback
, korzystając z tych 2 metod:
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) {} };
Zarejestruj wywołanie zwrotne, gdy utworzysz ustawienia transportu (zobacz metodę buildTransportControls()
), i wyrejestruj je, gdy aktywność się zakończy (w metodzie cyklu życia onStop()
aktywności).
Rozłącz się po zniszczeniu sesji multimediów
Jeśli sesja multimediów stanie się nieprawidłowa, wykonywane jest wywołanie zwrotne onSessionDestroyed()
. W takim przypadku sesja nie może zostać ponownie aktywowana w ciągu czasu zatrzymania MediaBrowserService
. Chociaż funkcje związane z MediaBrowser
mogą nadal działać, użytkownik nie może wyświetlać ani sterować odtwarzaniem z uszkodzonej sesji multimediów, co prawdopodobnie obniża wartość Twojej aplikacji.
Dlatego po zniszczeniu sesji musisz odłączyć się od serwera MediaBrowserService
, wywołując metodę disconnect()
.
Dzięki temu usługa przeglądarki nie będzie miała powiązanych klientów i może zostać zniszczona przez system operacyjny.
Jeśli później będzie trzeba ponownie połączyć się z MediaBrowserService
(na przykład aplikacja chce utrzymać trwałe połączenie z aplikacją do multimediów), utwórz nową instancję MediaBrowser
, zamiast używać starej.
Poniższy fragment kodu ilustruje implementację wywołania zwrotnego, która odłącza się od usługi przeglądarki po zniszczeniu sesji multimediów:
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 } };