MediaSessionService ile arka planda oynatma

Genellikle bir uygulama ön planda değilken medya oynatılması istenir. Örneğin, Örneğin, bir müzik çalar genellikle kullanıcı kilitlendiğinde veya başka bir uygulama kullanıyor olabilir. Media3 kitaplığı bir dizi arka planda oynatmayı destekleyen arabirimler kullanır.

MediaSessionService kullanma

Arka planda oynatmayı etkinleştirmek için Player ve MediaSession ayrı bir Hizmet içinde. Bu şekilde, uygulamanız kapalıyken bile cihaz medya yayınlamaya devam edebilir görebilirsiniz.

MediaSessionService, medya oturumunun ayrı olarak çalıştırılmasına izin verir
  uygulama etkinliğinden
Şekil 1: MediaSessionService, medyanın uygulamanın etkinliğinden ayrı olarak çalıştırılacak oturum

Bir oyuncuyu Hizmet'in içinde barındırırken MediaSessionService kullanmanız gerekir. Bunu yapmak için MediaSessionService öğesini genişleten bir sınıf oluşturun ve bir kontrol noktası olduğunu unutmayın.

MediaSessionService kullanımı, Google gibi harici müşterilerin işini kolaylaştırır Keşfetmek için Asistan, sistem medya kontrolleri veya Wear OS gibi tamamlayıcı cihazlar hizmete bağlanabilir, hizmete bağlanabilir ve oynatmayı kontrol edebilir. Üstelik tüm bunları kullanıcı arayüzü etkinliği. Hatta birden fazla istemci uygulaması birbirine bağlı olabilir. aynı anda aynı MediaSessionService öğesine; her uygulamanın kendine ait MediaController.

Hizmet yaşam döngüsünü uygulayın

Hizmetinizle ilgili üç yaşam döngüsü yöntemini uygulamanız gerekir:

  • onCreate(), ilk kumanda bağlanmak üzereyken çağrılır ve ve başlatıldığına dair bir ipucudur. Burası Player oluşturmak için en iyi yerdir MediaSession.
  • onTaskRemoved(Intent), kullanıcı uygulamayı son görevlerden bahsetmeye devam edelim. Oynatma işlemi devam ediyorsa uygulama, hizmeti kullanmaya devam etmeyi tercih edebilir. ön planda çalışıyor. Oynatıcı duraklatılmışsa hizmet şurada değildir: durdurulması gerekiyor.
  • Hizmet durdurulurken onDestroy() çağrılır. Tüm kaynaklar ve oturumun serbest bırakılması gerekir.
ziyaret edin.

Kotlin

class PlaybackService : MediaSessionService() {
  private var mediaSession: MediaSession? = null

  // Create your player and media session in the onCreate lifecycle event
  override fun onCreate() {
    super.onCreate()
    val player = ExoPlayer.Builder(this).build()
    mediaSession = MediaSession.Builder(this, player).build()
  }

  // The user dismissed the app from the recent tasks
  override fun onTaskRemoved(rootIntent: Intent?) {
    val player = mediaSession?.player!!
    if (!player.playWhenReady
        || player.mediaItemCount == 0
        || player.playbackState == Player.STATE_ENDED) {
      // Stop the service if not playing, continue playing in the background
      // otherwise.
      stopSelf()
    }
  }

  // Remember to release the player and media session in onDestroy
  override fun onDestroy() {
    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }
    super.onDestroy()
  }
}

Java

public class PlaybackService extends MediaSessionService {
  private MediaSession mediaSession = null;

  // Create your Player and MediaSession in the onCreate lifecycle event
  @Override
  public void onCreate() {
    super.onCreate();
    ExoPlayer player = new ExoPlayer.Builder(this).build();
    mediaSession = new MediaSession.Builder(this, player).build();
  }

  // The user dismissed the app from the recent tasks
  @Override
  public void onTaskRemoved(@Nullable Intent rootIntent) {
    Player player = mediaSession.getPlayer();
    if (!player.getPlayWhenReady()
        || player.getMediaItemCount() == 0
        || player.getPlaybackState() == Player.STATE_ENDED) {
      // Stop the service if not playing, continue playing in the background
      // otherwise.
      stopSelf();
    }
  }

  // Remember to release the player and media session in onDestroy
  @Override
  public void onDestroy() {
    mediaSession.getPlayer().release();
    mediaSession.release();
    mediaSession = null;
    super.onDestroy();
  }
}

Uygulama, arka planda oynatmayı devam ettirmenin alternatifi olarak Kullanıcı uygulamayı kapattığında her durumda hizmeti durdurma:

Kotlin

override fun onTaskRemoved(rootIntent: Intent?) {
  val player = mediaSession.player
  if (player.playWhenReady) {
    // Make sure the service is not in foreground.
    player.pause()
  }
  stopSelf()
}

Java

@Override
public void onTaskRemoved(@Nullable Intent rootIntent) {
  Player player = mediaSession.getPlayer();
  if (player.getPlayWhenReady()) {
    // Make sure the service is not in foreground.
    player.pause();
  }
  stopSelf();
}

Medya oturumuna erişim izni ver

Diğer istemcilerin medyanıza erişmesine izin vermek için onGetSession() yöntemini geçersiz kılın oturumdaki bilgileri sunar.

Kotlin

class PlaybackService : MediaSessionService() {
  private var mediaSession: MediaSession? = null
  // [...] lifecycle methods omitted

  override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
    mediaSession
}

Java

public class PlaybackService extends MediaSessionService {
  private MediaSession mediaSession = null;
  // [...] lifecycle methods omitted

  @Override
  public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
    return mediaSession;
  }
}

Manifest dosyasında hizmeti beyan edin

Bir uygulamanın, ön plan hizmetini çalıştırmak için izne ihtiyacı var. URL'yi manifest dosyası için FOREGROUND_SERVICE izninizi kontrol edin ve API 34 ile yukarıdaki de FOREGROUND_SERVICE_MEDIA_PLAYBACK üzerinde:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

Manifest'te Service sınıfınızı bir intent filtresiyle de bildirmeniz gerekir. / MediaSessionService.

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

Hedef kitlenizin kim olduğunu foregroundServiceType Uygulamanız Android yüklü bir cihazda çalışırken mediaPlayback dahil 10 (API düzeyi 29) ve sonraki sürümler.

Çalmayı MediaController kullanarak kontrol edin

Oynatıcı kullanıcı arayüzünüzü içeren Etkinlik veya Parçada bir MediaController kullanarak kullanıcı arayüzü ile medya oturumunuz arasında. Kullanıcı arayüzünüz komutlarını kullanıcı arayüzünden oynatıcıya kabul edilir. Bkz. MediaController oluşturun kullanma ve MediaController kullanma hakkında ayrıntılı bilgiler için kılavuza bakın.

Kullanıcı arayüzü komutlarını işleme

MediaSession, aşağıdakileri kullanarak kumandadan komut alır: MediaSession.Callback. MediaSession ilk kez başlatıldığında varsayılan oluşturulur tüm geçiş işlemlerini otomatik olarak işleyen MediaSession.Callback MediaController tarafından oynatıcınıza gönderdiği komutlar arasındadır.

Bildirim

MediaSessionService, sizin için otomatik olarak aşağıdaki özelliklere sahip bir MediaNotification oluşturur: çoğu durumda işe yarar. Varsayılan olarak, yayınlanan bildirim MediaStyle bildirim en son bilgilerle güncellenen bir oynatma kontrollerini görüntüler. MediaNotification oturumunuzun farkındadır ve diğer uygulamaların oynatmayı kontrol etmek için kullanılabilir arasında bağlantı oluşturur.

Örneğin, MediaSessionService kullanan bir müzik yayın uygulaması Öğeye ait başlığı, sanatçıyı ve albüm resmini gösteren MediaNotification bağlı olarak, oynatma kontrolleriyle birlikte oynatılan mevcut medya öğesi MediaSession yapılandırması.

Gerekli meta veriler medyada sağlanabilir veya medya öğesini seçin:

Kotlin

val mediaItem =
    MediaItem.Builder()
      .setMediaId("media-1")
      .setUri(mediaUri)
      .setMediaMetadata(
        MediaMetadata.Builder()
          .setArtist("David Bowie")
          .setTitle("Heroes")
          .setArtworkUri(artworkUri)
          .build()
      )
      .build()

mediaController.setMediaItem(mediaItem)
mediaController.prepare()
mediaController.play()

Java

MediaItem mediaItem =
    new MediaItem.Builder()
        .setMediaId("media-1")
        .setUri(mediaUri)
        .setMediaMetadata(
            new MediaMetadata.Builder()
                .setArtist("David Bowie")
                .setTitle("Heroes")
                .setArtworkUri(artworkUri)
                .build())
        .build();

mediaController.setMediaItem(mediaItem);
mediaController.prepare();
mediaController.play();

Uygulamalar, Android Medya denetimlerinin komut düğmelerini özelleştirebilir. Devamını okuyun özelleştirme konusunda kontrol eder.

Bildirim özelleştirme

Bildirimi özelleştirmek için bir oluşturun MediaNotification.Provider DefaultMediaNotificationProvider.Builder ile veya sağlayıcı arayüzünün özel bir uygulamasını oluşturarak. MediaSessionService ile kullanmak üzere setMediaNotificationProvider

Oynatmayı devam ettirme

Medya düğmeleri, Android cihazlarda ve diğer çevre birimlerinde bulunan donanım düğmeleridir. cihazları (örneğin, Bluetooth mikrofonlu kulaklıktaki oynat veya duraklat düğmesi) kullanın. Medya3 hizmet çalışırken medya düğmesi girişlerini sizin için işler.

Media3 medya düğmesi alıcısını bildir

Media3, kullanıcıların bir uygulama sonlandırıldıktan sonra ve cihaz tamamlandıktan sonra bile oynatma yeniden başlatıldı. Varsayılan olarak, oynatmayı devam ettirme kapalıdır. Bu, kullanıcının hizmetiniz çalışmadığında oynatmaya devam edilemez. Etkinleştirmek için önce manifest dosyanızda MediaButtonReceiver belirtmeniz gerekir:

<receiver android:name="androidx.media3.session.MediaButtonReceiver"
  android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MEDIA_BUTTON" />
  </intent-filter>
</receiver>

Oynatmayı devam ettirme geri çağırmasını uygula

Bluetooth cihazı veya Android sistem kullanıcı arayüzünün devam ettirme özelliği, onPlaybackResumption() çağrılır.

Kotlin

override fun onPlaybackResumption(
    mediaSession: MediaSession,
    controller: ControllerInfo
): ListenableFuture<MediaItemsWithStartPosition> {
  val settable = SettableFuture.create<MediaItemsWithStartPosition>()
  scope.launch {
    // Your app is responsible for storing the playlist and the start position
    // to use here
    val resumptionPlaylist = restorePlaylist()
    settable.set(resumptionPlaylist)
  }
  return settable
}

Java

@Override
public ListenableFuture<MediaItemsWithStartPosition> onPlaybackResumption(
    MediaSession mediaSession,
    ControllerInfo controller
) {
  SettableFuture<MediaItemsWithStartPosition> settableFuture = SettableFuture.create();
  settableFuture.addListener(() -> {
    // Your app is responsible for storing the playlist and the start position
    // to use here
    MediaItemsWithStartPosition resumptionPlaylist = restorePlaylist();
    settableFuture.set(resumptionPlaylist);
  }, MoreExecutors.directExecutor());
  return settableFuture;
}

Oynatma hızı, tekrarlama modu veya karıştırma modu. onPlaybackResumption(), oynatıcıyı yapılandırmak için iyi bir yerdir medya 3 oynatıcıyı hazırlamadan önce bu parametrelerle çalışır ve geri arama tamamlanır.

Gelişmiş kumanda yapılandırması ve geriye dönük uyumluluk

Yaygın bir senaryo, kontrol etmek için uygulamanın kullanıcı arayüzünde MediaController kullanmaktır. oynatma ve oynatma listesini görüntüleme. Aynı anda oturum açılmış durumda Google Asistan gibi harici istemcilere veri aktarıyor. Saatler için Wear OS ve arabalarda Android Auto. Media3 oturum demo uygulaması , bu tür bir senaryo uygulayan uygulamalara örnek gösterilebilir.

Bu harici istemciler eskinin MediaControllerCompat gibi API'leri kullanabilir AndroidX kitaplığı veya Android'in android.media.session.MediaController sürümü bahsedeceğim. Media3 eski kitaplıkla tamamen geriye dönük uyumludur ve Android Framework API ile birlikte çalışabilirlik özelliği sunar.

Medya bildirim denetleyicisini kullanma

Bu eski veya çerçeve denetleyicilerinin PlaybackState.getActions() çerçevesinden aynı değerlere ve PlaybackState.getCustomActions(). Şu öğenin eylemlerini ve özel işlemlerini belirlemek için: çerçeve oturumunda, uygulamalar medya bildirim denetleyicisini kullanabilir. ve kullanılabilir komutlarını ve özel düzenini ayarlayın. Hizmet, medya dosyalarını için bir bildirim denetleyicisi kullanıyorsanız, oturumda ConnectionResult, yapılandırmak için geri aramanızın onConnect() tarafından döndürüldü çerçeve oturumunun eylemlerini ve özel eylemlerini içerir.

Yalnızca mobil kullanıma yönelik bir senaryo göz önünde bulundurulduğunda bir uygulama, Mevcut komutları ayarlamak için MediaSession.Callback.onConnect() çerçeve oturumu için özel olarak aşağıdaki gibi özel düzen:

Kotlin

override fun onConnect(
  session: MediaSession,
  controller: MediaSession.ControllerInfo
): ConnectionResult {
  if (session.isMediaNotificationController(controller)) {
    val sessionCommands =
      ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(customCommandSeekBackward)
        .add(customCommandSeekForward)
        .build()
    val playerCommands =
      ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
        .remove(COMMAND_SEEK_TO_PREVIOUS)
        .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
        .remove(COMMAND_SEEK_TO_NEXT)
        .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
        .build()
    // Custom layout and available commands to configure the legacy/framework session.
    return AcceptedResultBuilder(session)
      .setCustomLayout(
        ImmutableList.of(
          createSeekBackwardButton(customCommandSeekBackward),
          createSeekForwardButton(customCommandSeekForward))
      )
      .setAvailablePlayerCommands(playerCommands)
      .setAvailableSessionCommands(sessionCommands)
      .build()
  }
  // Default commands with default custom layout for all other controllers.
  return AcceptedResultBuilder(session).build()
}

Java

@Override
public ConnectionResult onConnect(
    MediaSession session, MediaSession.ControllerInfo controller) {
  if (session.isMediaNotificationController(controller)) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS
            .buildUpon()
            .add(customCommandSeekBackward)
            .add(customCommandSeekForward)
            .build();
    Player.Commands playerCommands =
        ConnectionResult.DEFAULT_PLAYER_COMMANDS
            .buildUpon()
            .remove(COMMAND_SEEK_TO_PREVIOUS)
            .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
            .remove(COMMAND_SEEK_TO_NEXT)
            .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
            .build();
    // Custom layout and available commands to configure the legacy/framework session.
    return new AcceptedResultBuilder(session)
        .setCustomLayout(
            ImmutableList.of(
                createSeekBackwardButton(customCommandSeekBackward),
                createSeekForwardButton(customCommandSeekForward)))
        .setAvailablePlayerCommands(playerCommands)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
  // Default commands without default custom layout for all other controllers.
  return new AcceptedResultBuilder(session).build();
}

Android Auto'yu özel komutlar göndermesi için yetkilendirin

MediaLibraryService kullanırken Android Auto'yu mobil uygulamada desteklemek için Android Auto kumandası kullanılabilir uygun komutlar gerektirir, aksi takdirde Media3 bunu reddeder. gelen özel komutlar:

Kotlin

override fun onConnect(
  session: MediaSession,
  controller: MediaSession.ControllerInfo
): ConnectionResult {
  val sessionCommands =
    ConnectionResult.DEFAULT_SESSION_AND_LIBRARY_COMMANDS.buildUpon()
      .add(customCommandSeekBackward)
      .add(customCommandSeekForward)
      .build()
  if (session.isMediaNotificationController(controller)) {
    // [...] See above.
  } else if (session.isAutoCompanionController(controller)) {
    // Available session commands to accept incoming custom commands from Auto.
    return AcceptedResultBuilder(session)
      .setAvailableSessionCommands(sessionCommands)
      .build()
  }
  // Default commands with default custom layout for all other controllers.
  return AcceptedResultBuilder(session).build()
}

Java

@Override
public ConnectionResult onConnect(
    MediaSession session, MediaSession.ControllerInfo controller) {
  SessionCommands sessionCommands =
      ConnectionResult.DEFAULT_SESSION_COMMANDS
          .buildUpon()
          .add(customCommandSeekBackward)
          .add(customCommandSeekForward)
          .build();
  if (session.isMediaNotificationController(controller)) {
    // [...] See above.
  } else if (session.isAutoCompanionController(controller)) {
    // Available commands to accept incoming custom commands from Auto.
    return new AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
  // Default commands without default custom layout for all other controllers.
  return new AcceptedResultBuilder(session).build();
}

Oturum demo uygulaması, otomotiv modülü, Bu makale, ayrı bir APK gerektiren Automotive OS desteği gösterir.