Uygulama ön planda değilken medya oynatılması genellikle istenir. Örneğin, bir müzik çalar genellikle kullanıcı cihazını kilitlediğinde veya başka bir uygulamayı 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 Hizmet içinde bulundurmalısınız.
Bu şekilde cihaz, uygulamanız ön planda değilken bile medya
yayınlamaya devam edebilir.
Hizmet içinde bir oyuncu barındırırken MediaSessionService
kullanmalısınız.
Bunu yapmak için MediaSessionService
` öğesini genişleten bir sınıf oluşturun ve içinde medya oturumunuzu oluşturun.
MediaSessionService
kullanıldığında Google Asistan, sistem medya denetimleri veya Wear OS gibi tamamlayıcı cihazlar, uygulamanızın kullanıcı arayüzü etkinliğine hiçbir şekilde erişmeden hizmetinizi keşfetmeyi, hizmete bağlanmayı ve oynatmayı kontrol etmeyi sağlayan harici istemciler tarafından kullanılabilir. Aslında aynı anda aynı MediaSessionService
cihazına bağlı birden fazla istemci uygulaması olabilir ve her uygulamanın kendi MediaController
sahibi olabilir.
Hizmet yaşam döngüsünü uygulayın
Hizmetinizin üç yaşam döngüsü yöntemini uygulamanız gerekir:
onCreate()
, ilk denetleyici bağlanmak üzereyken ve hizmet başlatılıp başlatıldığında çağrılır. BurasıPlayer
veMediaSession
derlemek için en iyi yerdir.- Kullanıcı, uygulamayı son görevlerden çıkardığında
onTaskRemoved(Intent)
çağrılır. Oynatma devam ediyorsa uygulama, hizmeti ön planda çalıştırmayı seçebilir. Oynatıcı duraklatılırsa hizmet ön planda değildir ve durdurulması gerekir. - Hizmet durdurulduğunda
onDestroy()
çağrılır. Oynatıcı ve oturum dahil tüm kaynakların serbest bırakılması gerekir.
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(); } }
Oynatmanın arka planda devam etmesini sağlamak yerine, kullanıcı uygulamayı kapattığında bir uygulama her durumda hizmeti durdurabilir:
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 verin
Hizmet oluşturulurken oluşturulan medya oturumunuza diğer istemcilere erişim izni vermek 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; } }
Manifest dosyasında hizmeti tanımlama
Bir uygulamanın ön plan hizmetini çalıştırmak için izne ihtiyacı var. FOREGROUND_SERVICE
iznini manifest dosyasına ekleyin. API 34 ve üstünü hedefliyorsanız de FOREGROUND_SERVICE_MEDIA_PLAYBACK
:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
Ayrıca, manifest dosyasında MediaSessionService
intent filtresiyle Service
sınıfınızı bildirmeniz gerekir.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
Uygulamanız Android 10 (API düzeyi 29) ve sonraki sürümleri çalıştıran bir cihazda çalışırken mediaPlayback
içeren bir foregroundServiceType
tanımlamanız gerekir.
MediaController
kullanarak oynatmayı kontrol etme
Oynatıcı kullanıcı arayüzünüzü içeren Etkinlik veya Bölüm'de, bir MediaController
kullanarak kullanıcı arayüzü ve medya oturumunuz arasında bir bağlantı oluşturabilirsiniz. Kullanıcı arayüzünüz, kullanıcı arayüzünden oturum içindeki oynatıcıya komut göndermek için medya denetleyiciyi kullanır. MediaController
oluşturma ve kullanma hakkında ayrıntılı bilgi için MediaController
oluşturma kılavuzuna bakın.
Kullanıcı arayüzü komutlarını işleme
MediaSession
, MediaSession.Callback
üzerinden kumandadan komut alır. Bir MediaSession
başlatıldığında, MediaController
tarafından oynatıcınıza gönderilen tüm komutları otomatik olarak işleyen varsayılan bir MediaSession.Callback
uygulaması oluşturulur.
Bildirim
MediaSessionService
, sizin için otomatik olarak ç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 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 yayın uygulaması, MediaSession
yapılandırmanıza bağlı olarak oynatma kontrolleriyle birlikte oynatılan mevcut medya öğesinin başlığını, sanatçısını ve albüm resmini görüntüleyen bir MediaNotification
oluşturur.
Gerekli meta veriler medyada sağlanabilir veya aşağıdaki snippet'te olduğu gibi medya öğesinin bir parçası olarak açıklanabilir:
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 kontrollerinin komut düğmelerini özelleştirebilir. Android Medya denetimlerini özelleştirme hakkında daha fazla bilgi edinin.
Bildirim özelleştirme
Bildirimi ö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
cihazınıza ekleyin.
Oynatmayı devam ettirme
Medya düğmeleri, Android cihazlarda ve diğer çevre birimi cihazlarında bulunan donanım düğmeleridir (örneğin, Bluetooth mikrofonlu kulaklıktaki oynat veya duraklat düğmesi). Media3, hizmet çalışırken medya düğmesi girişlerini sizin için işler.
Media3 medya düğmesi alıcısını bildirme
Media3, kullanıcıların uygulama sonlandırıldıktan ve hatta cihaz yeniden başlatıldıktan sonra oynatmaya devam edebilmelerini sağlayan bir API içerir. Oynatmayı devam ettirme varsayılan olarak kapalıdır. Bu, hizmetiniz çalışmıyorken kullanıcının oynatmayı devam ettiremeyeceği anlamına gelir. Kaydolmak için manifest dosyanızdaki MediaButtonReceiver
özelliğini 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ğırmasını uygula
Bir Bluetooth cihaz veya Android Sistem Kullanıcı Arayüzü devam ettirme özelliği tarafından oynatma devam ettirme 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ıştırma modu gibi başka parametreler depoladıysanız onPlaybackResumption()
, Media3 oynatıcıyı hazırlamadan ve geri çağırma tamamlandığında oynatmaya başlamadan önce oynatıcıyı bu parametrelerle yapılandırmak için iyi bir yerdir.
Gelişmiş denetleyici yapılandırması ve geriye dönük uyumluluk
Yaygın bir senaryo, oynatmayı kontrol etmek ve oynatma listesini görüntülemek için uygulama kullanıcı arayüzünde bir MediaController
kullanmaktır. Aynı zamanda oturumda; Android medya kontrolleri ve mobil cihazlarda ya da TV'de Asistan, kol saatleri için Wear OS ve arabalarda Android Auto gibi harici istemciler de oturumda gösterilir. Media3 oturum demo uygulaması, böyle bir senaryoyu uygulayan uygulamalara örnek olarak verilebilir.
Bu harici istemciler, eski AndroidX kitaplığının MediaControllerCompat
veya Android çerçevesinin android.media.session.MediaController
gibi API'lerini kullanabilir. Media3, eski kitaplıkla tamamen geriye uyumludur ve Android Framework API ile birlikte çalışabilir.
Medya bildirim denetleyicisini kullanma
Bu eski veya çerçeve denetleyicilerinin PlaybackState.getActions()
ve PlaybackState.getCustomActions()
çerçevelerinden aynı değerleri okuduğunu anlamak önemlidir. Bir uygulama, çerçeve oturumunun işlemlerini ve özel işlemlerini belirlemek için medya bildirimi denetleyicisini kullanıp mevcut komutlarını ve özel düzenini ayarlayabilir. Hizmet, medya bildirim denetleyicisini oturumunuza bağlar ve oturum, çerçeve oturumundaki işlemleri ve özel işlemleri yapılandırmak için geri çağırmanızın onConnect()
işlevi tarafından döndürülen ConnectionResult
öğesini kullanır.
Yalnızca mobil cihazlara yönelik bir senaryoda uygulamalar, çerçeve oturumuna özel kullanılabilir komutları ve özel düzeni ayarlamak için aşağıdaki gibi MediaSession.Callback.onConnect()
uygulaması 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 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 yetkilendirme
MediaLibraryService
kullanılırken ve mobil uygulamayla Android Auto'yu desteklemek için Android Auto denetleyicisinin uygun komutları kullanması gerekir. Aksi takdirde Media3, bu kumandadan 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 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ında, ayrı bir APK gerektiren Automotive OS desteğini gösteren bir otomotiv modülü bulunur.