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.
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 gibi harici istemcilerin, sistem medya kontrolleri veya Wear OS gibi tamamlayıcı cihazların, 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
'e bağlı birden fazla istemci uygulaması olabilir. Bu uygulamalardan her biri kendi MediaController
'ına sahiptir.
Hizmet yaşam döngüsünü uygulama
Hizmetinizin üç 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
veMediaSession
oluşturmak için en iyi yerdir.- Kullanıcı, uygulamayı son görevlerden kaldırdığında
onTaskRemoved(Intent)
çağrılır. Oynatma devam ediyorsa uygulama, hizmeti ön planda çalıştırmayı seçebilir. Oynatıcı duraklatılmışsa hizmet ön planda değildir ve durdurulması gerekir. onDestroy()
, hizmet durdurulduğunda çağrılır. Oynatıcı ve oturum da dahil olmak üzere 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(); } }
Oynatma işlemini arka planda devam ettirmek yerine, uygulama kullanıcının uygulamayı kapatması durumunda 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 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
Uygulamanın ön plan hizmeti çalıştırması için izin gerekir. Manifest'e FOREGROUND_SERVICE
iznini, API 34 ve sonraki sürümleri hedefliyorsanız FOREGROUND_SERVICE_MEDIA_PLAYBACK
iznini de ekleyin:
<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 filtresiyle belirtmeniz 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 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 sırasında 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.
Kullanıcı arayüzü 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 uygulamaların oynatılmasını 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();
Uygulamalar, Android medya kontrollerinin komut düğmelerini özelleştirebilir. Android medya kontrollerini ö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
'ünüze ekleyin.
Oynatma işlemini devam ettirme
Medya düğmeleri, Android cihazlarda ve diğer çevre birimi cihazlarda bulunan donanım düğmeleridir (ör. Bluetooth kulaklıktaki oynatma veya duraklatma düğmesi). Media3, hizmet çalışırken medya düğmesi girişlerini sizin için yönetir.
Media3 medya düğmesi alıcısını tanımlama
Media3, kullanıcıların bir uygulama sona erdikten sonra ve hatta cihaz yeniden başlatıldıktan sonra oynatmayı devam ettirmelerini sağlayan bir API içerir. Oynatma devam ettirme özelliği varsayılan olarak devre dışıdır. Bu durumda, kullanıcı hizmetiniz çalışmadığında oynatmayı devam ettiremez. Özelliği etkinleştirmek için manifest dosyanızda 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>
Oynatmayı 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 karma 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()
iyi bir yerdir.
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 çerçevesinin android.media.session.MediaController
gibi API'leri kullanabilir. Media3, eski kitaplıkla tam olarak geriye dönük uyumludur ve Android framework API ile birlikte çalışabilirlik sağlar.
Medya bildirim denetleyicisini kullanma
Bu eski veya çerçeve denetleyicilerinin, PlaybackState.getActions()
ve PlaybackState.getCustomActions()
çerçevesinden aynı değerleri okuduğunu anlamanız önemlidir. Bir uygulama, çerçeve oturumunun işlemlerini ve özel işlemlerini belirlemek için medya bildirimi denetleyicisini kullanabilir ve mevcut komutlarını ve özel düzenini ayarlayabilir. Hizmet, medya bildirimi denetleyicisini oturumunuza bağlar ve oturum, çerçeve oturumunun işlemlerini ve özel işlemlerini yapılandırmak için geri çağırma işlevinizin onConnect()
tarafından döndürülen ConnectionResult
değerini kullanır.
Yalnızca mobil cihazlara yönelik bir senaryoda, bir uygulama, mevcut komutları ve özel düzeni çerçeve oturumuna özel olarak ayarlamak için 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 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'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 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.