MediaSessionService ile arka planda oynatma

Bir uygulama ön planda değilken medyayı oynatmak genellikle istenir. Örneğin, müzik çalarlar genellikle kullanıcı cihazını kilitlediğinde veya başka bir uygulama kullanırken müzik çalmaya devam eder. Media3 kitaplığı, arka planda oynatmayı desteklemenize olanak tanıyan bir dizi arayüz sağlar.

MediaSessionService kullanma

Arka planda oynatmayı etkinleştirmek için Player ve MediaSession öğelerini ayrı bir Service içinde bulundurmanız gerekir. Bu sayede, uygulamanız ön planda olmasa bile cihaz medya sunmaya devam edebilir.

MediaSessionService, medya oturumunun uygulamanın etkinliğinden ayrı olarak çalışmasına olanak tanır.
Şekil 1: MediaSessionService, medya oturumunun uygulamanın etkinliğinden ayrı olarak çalışmasına olanak tanır

Bir oyuncuyu hizmet içinde barındırırken MediaSessionService kullanmanız gerekir. Bunu yapmak için MediaSessionService sınıfını genişleten bir sınıf oluşturun ve medya oturumunuzu bu sınıfın içinde oluşturun.

MediaSessionService kullanıldığında Google Asistan, sistem medya kontrolleri, çevre birimlerindeki medya düğmeleri veya Wear OS gibi yardımcı cihazlar gibi harici istemciler, uygulamanızın kullanıcı arayüzü etkinliğine hiç erişmeden hizmetinizi keşfedebilir, hizmetinize bağlanabilir ve oynatmayı kontrol edebilir. Hatta aynı anda aynı MediaSessionService öğesine bağlı birden fazla istemci uygulaması olabilir ve her uygulamanın kendi MediaController öğesi bulunur.

Hizmet yaşam döngüsünü uygulama

Hizmetinizin iki yaşam döngüsü yöntemini uygulamanız gerekir:

  • onCreate(), ilk kumanda bağlanmak üzereyken ve hizmet oluşturulup başlatıldığında çağrılır. Player ve MediaSession oluşturmak için en iyi yerdir.
  • onDestroy(), hizmet durdurulurken çağrılır. Oyuncu ve oturum dahil tüm kaynakların serbest bırakılması gerekir.

İsteğe bağlı olarak, kullanıcının uygulamayı son görevlerden kapattığında ne olacağını özelleştirmek için onTaskRemoved(Intent) değerini geçersiz kılabilirsiniz. Varsayılan olarak, oynatma devam ediyorsa hizmet çalışmaya devam eder, aksi takdirde durdurulur.

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

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

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

Arka planda oynatmaya devam etmek yerine, kullanıcı uygulamayı kapattığında hizmeti durdurabilirsiniz:

Kotlin

override fun onTaskRemoved(rootIntent: Intent?) {
  pauseAllPlayersAndStopSelf()
}

Java

@Override
public void onTaskRemoved(@Nullable Intent rootIntent) {
  pauseAllPlayersAndStopSelf();
}

onTaskRemoved'nın diğer tüm manuel uygulamaları için, oynatmanın devam edip etmediğini ve ön plan hizmetinin başlatılıp başlatılmadığını kontrol etmek üzere isPlaybackOngoing() kullanabilirsiniz.

Medya oturumuna erişim sağlama

Hizmet oluşturulduğunda oluşturulan medya oturumunuza diğer istemcilerin erişebilmesi için onGetSession() yöntemini geçersiz kılın.

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

Hizmeti manifestte bildirin

Bir uygulamanın oynatma ön plan hizmetini çalıştırmak için FOREGROUND_SERVICE ve FOREGROUND_SERVICE_MEDIA_PLAYBACK izinleri gerekir:

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

Ayrıca, manifest dosyasında Service sınıfınızı MediaSessionService intent filtresi ve mediaPlayback içeren bir foregroundServiceType ile birlikte beyan etmeniz gerekir.

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

MediaController kullanarak oynatmayı kontrol etme

Oynatıcı kullanıcı arayüzünüzü içeren Etkinlik veya Parçada, MediaController kullanarak kullanıcı arayüzü ile medya oturumunuz arasında bir bağlantı oluşturabilirsiniz. Kullanıcı arayüzünüz, oturumdaki oynatıcıya komut göndermek için medya denetleyicisini kullanır. MediaController oluşturma ve kullanma hakkında ayrıntılı bilgi için MediaController oluşturma rehberine bakın.

Herkese açık kullanıcı adı MediaController komutları

MediaSession, MediaSession.Callback aracılığıyla kontrol cihazından komut alır. MediaSession başlatıldığında, MediaSession.Callback için varsayılan bir uygulama oluşturulur. Bu uygulama, MediaController öğesinin oynatıcınıza gönderdiği tüm komutları otomatik olarak işler.

Bildirim

MediaSessionService, çoğu durumda işe yarayacak bir MediaNotification oluşturur. Yayınlanan bildirim, varsayılan olarak medya oturumunuzdaki en son bilgilerle güncellenen ve oynatma kontrollerini gösteren bir MediaStyle bildirimidir. MediaNotification, oturumunuzu bilir ve aynı oturuma bağlı diğer uygulamalarda oynatmayı kontrol etmek için kullanılabilir.

Örneğin, MediaSessionService kullanan bir müzik akışı uygulaması, MediaSession yapılandırmanıza göre oynatma kontrollerinin yanı sıra, çalınan mevcut medya öğesinin başlığını, sanatçısını ve albüm kapağını gösteren bir MediaNotification oluşturur.

Gerekli meta veriler medyada sağlanabilir veya aşağıdaki snippet'te gösterildiği gibi medya öğesinin bir parçası olarak beyan edilebilir:

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

Bildirim yaşam döngüsü

Bildirim, oynatma listesinde Player MediaItem örneği olduğunda oluşturulur.

Tüm bildirim güncellemeleri, Player ve MediaSession durumuna göre otomatik olarak gerçekleşir.

Ön plan hizmeti çalışırken bildirim kaldırılamaz. Bildirimi hemen kaldırmak için Player.release() numaralı telefonu aramanız veya Player.clearMediaItems() simgesini kullanarak oynatma listesini temizlemeniz gerekir.

Oynatıcı, 10 dakikadan uzun süre duraklatılır, durdurulur veya başarısız olursa ve kullanıcı etkileşimi olmazsa hizmet, sistem tarafından yok edilebilmesi için otomatik olarak ön plan hizmeti durumundan çıkarılır. Kullanıcının hizmet yaşam döngüsünü yeniden başlatmasına ve daha sonra oynatmaya devam etmesine izin vermek için oynatmaya devam etme özelliğini uygulayabilirsiniz.

Bildirim özelleştirme

Şu anda oynatılan öğeyle ilgili meta veriler, MediaItem.MediaMetadata değiştirilerek özelleştirilebilir. Mevcut bir öğenin meta verilerini güncellemek istiyorsanız Player.replaceMediaItem simgesini kullanarak oynatmayı kesintiye uğratmadan meta verileri güncelleyebilirsiniz.

Android medya kontrolleri için özel medya düğmesi tercihleri ayarlayarak bildirimde gösterilen düğmelerden bazılarını da özelleştirebilirsiniz. Android'de medya kontrollerini özelleştirme hakkında daha fazla bilgi edinin.

Bildirimi daha da özelleştirmek için DefaultMediaNotificationProvider.Builder ile MediaNotification.Provider oluşturun veya sağlayıcı arayüzünün özel bir uygulamasını oluşturun. setMediaNotificationProvider ile sağlayıcınızı MediaSessionService'ya ekleyin.

Oynatmaya devam etme

MediaSessionService sonlandırıldıktan ve cihaz yeniden başlatıldıktan sonra bile, kullanıcılara hizmeti yeniden başlatıp oynatmaya kaldıkları yerden devam etmelerini sağlamak için oynatmaya devam etme seçeneği sunulabilir. Oynatmaya devam etme özelliği varsayılan olarak devre dışıdır. Bu nedenle, hizmetiniz çalışmıyorken kullanıcı oynatmaya devam edemez. Bu özelliği etkinleştirmek için bir medya düğmesi alıcısı bildirmeniz ve onPlaybackResumption yöntemini uygulamanız gerekir.

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

Manifestinizde MediaButtonReceiver öğesini beyan ederek başlayın:

<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ı uygulama

Oynatmaya devam etme isteği bir Bluetooth cihazı veya Android Sistem Kullanıcı Arayüzü devam ettirme özelliği tarafından gönderildiğinde onPlaybackResumption() geri çağırma yöntemi ç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, metadata (like title
    // and artwork) of the current item 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, metadata (like title
    // and artwork) of the current item and the start position to use here.
    MediaItemsWithStartPosition resumptionPlaylist = restorePlaylist();
    settableFuture.set(resumptionPlaylist);
  }, MoreExecutors.directExecutor());
  return settableFuture;
}

Oynatma hızı, tekrar modu veya karıştırma modu gibi başka parametreler sakladıysanız onPlaybackResumption(), geri çağırma tamamlandığında Media3 oynatıcıyı hazırlayıp oynatmaya başlamadan önce oynatıcıyı bu parametrelerle yapılandırmak için iyi bir yerdir.

Bu yöntem, cihaz yeniden başlatıldıktan sonra Android sistem kullanıcı arayüzü devam ettirme bildirimini oluşturmak için önyükleme sırasında çağrılır. Zengin bildirim için, ağ erişimi henüz kullanılamıyor olabileceğinden mevcut öğenin MediaMetadata gibi alanlarını title ve artworkData veya artworkUri yerel olarak kullanılabilen değerlerle doldurmanız önerilir. Oynatma konumunun devam ettirileceğini belirtmek için MediaMetadata.extras öğesine MediaConstants.EXTRAS_KEY_COMPLETION_STATUS ve MediaConstants.EXTRAS_KEY_COMPLETION_PERCENTAGE simgelerini de ekleyebilirsiniz.

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

Sık karşılaşılan bir senaryo, oynatmayı kontrol etmek ve oynatma listesini göstermek için uygulama kullanıcı arayüzünde MediaController kullanmaktır. Aynı zamanda oturum; Android'deki medya kontrolleri, mobil cihazlarda veya TV'de Asistan, kol saatlerinde Wear OS ve arabalarda Android Auto gibi harici istemcilere de sunulur. Media3 oturum demo uygulaması bu tür bir senaryoyu uygulayan bir uygulamaya örnektir.

Bu harici istemciler, eski AndroidX kitaplığının MediaControllerCompat veya Android platformunun android.media.session.MediaController gibi API'leri kullanabilir. Media3, eski kitaplıkla tamamen geriye dönük uyumludur ve Android platform API'siyle birlikte çalışabilir.

Medya bildirimi denetleyicisini kullanma

Bu eski ve platform denetleyicilerinin aynı durumu paylaştığını ve görünürlüğün denetleyiciye göre özelleştirilemediğini (örneğin, kullanılabilir PlaybackState.getActions() ve PlaybackState.getCustomActions()) anlamak önemlidir. Bu eski ve platform denetleyicileriyle uyumluluk için platform medya oturumunda ayarlanan durumu yapılandırmak üzere medya bildirimi denetleyicisini kullanabilirsiniz.

Örneğin, bir uygulama, kullanılabilir komutları ve medya düğmesi tercihlerini özellikle platform oturumu için aşağıdaki şekilde ayarlamak üzere MediaSession.Callback.onConnect() uygulamasını sağlayabilir:

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 button preferences and commands to configure the platform session.
    return AcceptedResultBuilder(session)
      .setMediaButtonPreferences(
        ImmutableList.of(
          createSeekBackwardButton(customCommandSeekBackward),
          createSeekForwardButton(customCommandSeekForward))
      )
      .setAvailablePlayerCommands(playerCommands)
      .setAvailableSessionCommands(sessionCommands)
      .build()
  }
  // Default commands with default button preferences 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 button preferences and commands to configure the platform session.
    return new AcceptedResultBuilder(session)
        .setMediaButtonPreferences(
            ImmutableList.of(
                createSeekBackwardButton(customCommandSeekBackward),
                createSeekForwardButton(customCommandSeekForward)))
        .setAvailablePlayerCommands(playerCommands)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
  // Default commands with default button preferences for all other controllers.
  return new AcceptedResultBuilder(session).build();
}

Android Auto'nun özel komutlar göndermesine izin verme

MediaLibraryService kullanırken ve mobil uygulamayla Android Auto'yu desteklemek için Android Auto denetleyicisinin uygun komutlara sahip olması gerekir. Aksi takdirde Media3, bu denetleyiciden gelen özel komutları reddeder:

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 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 for all other controllers.
  return new AcceptedResultBuilder(session).build();
}

Oturum demo uygulamasında, ayrı bir APK gerektiren Automotive OS desteğini gösteren bir otomotiv modülü bulunur.