MediaSessionService ile arka planda oynatma

Uygulama ön planda değilken medya oynatmak genellikle istenir. Örneğin, müzik çalar genellikle kullanıcı cihazını kilitlediğinde veya başka bir uygulamayı kullanırken müziği ç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 Hizmet içine eklemeniz gerekir. Bu sayede cihaz, uygulamanız ön planda değilken bile medya yayınlamaya 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 Hizmet içinde oyuncu barındırırken MediaSessionService kullanmanız gerekir. Bunun 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'ü kullanmak, Google Asistan, sistem medya kontrolleri, çevre birimleri üzerindeki medya düğmeleri veya Wear OS gibi tamamlayıcı cihazlar gibi harici istemcilerin, uygulamanızın kullanıcı arayüzü etkinliğine hiç erişmeden hizmetinizi keşfetmesine, hizmetinize bağlanmasına ve oynatmayı kontrol etmesine olanak tanır. Aslında, aynı anda aynı MediaSessionService'ye bağlı birden fazla istemci uygulaması olabilir. Bu uygulamalardan her biri kendi MediaController'sine sahiptir.

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 durdurulduğunda çağrılır. Oynatıcı ve oturum dahil tüm kaynakların serbest bırakılması gerekir.

Kullanıcı uygulamayı son görevlerden kaldırdığında ne olacağını özelleştirmek için isteğe bağlı olarak 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();
  }
}

Oynatma işlemini arka planda devam ettirmek 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 manuel olarak uygulanmasıyla ilgili diğer durumlarda, oynatmanın devam ediyor olarak kabul edilip edilmediğini ve ön plan hizmetinin başlatılıp başlatılmadığını kontrol etmek için isPlaybackOngoing()'ü kullanabilirsiniz.

Medya oturumuna erişim izni verme

Diğer istemcilerin, hizmet oluşturulduğunda oluşturulan medya oturumunuza 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 manifest'te tanımlama

Bir uygulamanın oynatma ön plan hizmetini çalıştırması 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, Service sınıfınızı manifest dosyasında MediaSessionService intent filtresi ve mediaPlayback içeren bir foregroundServiceType ile de tanımlamanız 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 etkinlikte veya fragmanda, MediaController kullanarak kullanıcı arayüzü ile medya oturumunuz arasında bir bağlantı oluşturabilirsiniz. Kullanıcı arayüzünüz, oturum içinde kullanıcı arayüzünüzden oynatıcıya komut göndermek için medya kontrol cihazını kullanır. MediaController oluşturma ve kullanmayla ilgili ayrıntılar için MediaController oluşturma kılavuzuna bakın.

MediaController komutlarını işleme

MediaSession, MediaSession.Callback üzerinden kontrolörden komut alır. Bir MediaSession'ü başlattığınızda, MediaController'nin oynatıcınıza gönderdiği tüm komutları otomatik olarak işleyen varsayılan bir MediaSession.Callback uygulaması oluşturulur.

Bildirim

MediaSessionService, çoğu durumda işe yarayacak bir MediaNotification'ı sizin için otomatik olarak oluşturur. Varsayılan olarak yayınlanan bildirim, medya oturumunuzun en son bilgileriyle güncellenen ve oynatma kontrollerini gösteren bir MediaStyle bildirimidir. MediaNotification, oturumunuzun farkındadır 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 oynatılan mevcut medya öğesinin başlığını, sanatçısını ve albüm resmini gösteren bir MediaNotification oluşturur.

Gerekli meta veriler, medya öğesinde sağlanabilir veya aşağıdaki snippet'te gösterildiği gibi medya öğesinin parçası olarak tanımlanabilir:

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, Player oynatma listesinde MediaItem öğe 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()'ü çağırmanız veya Player.clearMediaItems()'ü kullanarak oynatma listesini temizlemeniz gerekir.

Oynatıcı, başka kullanıcı etkileşimi olmadan 10 dakikadan uzun süre duraklatılır, durdurulur veya başarısız olursa sistem tarafından kaldırılabilmesi için hizmet otomatik olarak ön plan hizmet durumundan çıkarılır. Kullanıcının hizmet yaşam döngüsünü yeniden başlatmasına ve daha sonra oynatmayı devam ettirmesine izin vermek için oynatma işlemini devam ettirme ö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 oynatmayı kesintiye uğratmadan meta verileri güncellemek için Player.replaceMediaItem simgesini kullanabilirsiniz.

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

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

Oynatma işlemini devam ettirme

MediaSessionService sonlandırıldıktan ve hatta cihaz yeniden başlatıldıktan sonra bile kullanıcıların hizmeti yeniden başlatmasına ve bıraktıkları yerden oynatmaya devam etmesine olanak tanımak için oynatma işlemini devam ettirme seçeneği sunulabilir. Oynatma işlemini devam ettirme özelliği varsayılan olarak devre dışıdır. Bu, kullanıcının hizmetiniz çalışmadığında oynatmayı devam ettiremeyeceği anlamına gelir. Bu özelliği etkinleştirmek için bir medya düğmesi alıcısını tanımlamanız ve onPlaybackResumption yöntemini uygulamanız gerekir.

Media3 medya düğmesi alıcısını tanımlama

Manifestinizde MediaButtonReceiver öğesini tanımlayarak 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>

Oynatma devam ettirme geri çağırma işlevini uygulama

Oynatma işleminin devam ettirilmesi bir Bluetooth cihaz veya Android sistem kullanıcı arayüzü devam ettirme özelliği tarafından istendiğ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 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ı, tekrar modu veya karışık mod gibi başka parametreler depoladıysanız Media3 oynatıcıyı hazırlamadan ve geri çağırma tamamlandığında oynatmayı başlatmadan önce oynatıcıyı bu parametrelerle yapılandırmak için onPlaybackResumption()'ü kullanabilirsiniz.

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

Çalmayı kontrol etmek ve oynatma listesini görüntülemek için uygulama kullanıcı arayüzünde MediaController kullanılması yaygın bir durumdur. Aynı zamanda oturum, Android medya kontrolleri ve mobil cihazlarda veya TV'de Asistan, kol saatleri için Wear OS ve arabalarda Android Auto gibi harici istemcilere gösterilir. Media3 oturum demo uygulaması, bu tür bir senaryoyu uygulayan bir uygulama örneğidir.

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ışabilirlik sağlar.

Medya bildirim denetleyicisini kullanma

Bu eski ve platform denetleyicilerinin aynı durumu paylaştığını ve görünürlüğün denetleyiciye göre özelleştirilemeyeceğini (ör. mevcut PlaybackState.getActions() ve PlaybackState.getCustomActions()) bilmek önemlidir. Platform medya oturumunda ayarlanan durumu bu eski ve platform denetleyicileriyle uyumlu olacak şekilde yapılandırmak için medya bildirimi denetleyicisini kullanabilirsiniz.

Örneğin, bir uygulama, kullanılabilir komutları ve medya düğmesi tercihlerini özellikle platform oturumu için ayarlamak üzere MediaSession.Callback.onConnect() uygulamasını aşağıdaki gibi 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'ya özel komutlar gönderme yetkisi verme

MediaLibraryService kullanırken ve Android Auto'yu mobil uygulamayla 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.