Tworzenie klienta przeglądarki multimediów

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