Media sebaiknya diputar saat aplikasi tidak berada di latar depan. Misalnya, pemutar musik umumnya terus memutar musik saat pengguna telah mengunci perangkat atau menggunakan aplikasi lain. Library Media3 menyediakan serangkaian antarmuka yang memungkinkan Anda mendukung pemutaran di latar belakang.
Menggunakan MediaSessionService
Untuk mengaktifkan pemutaran di latar belakang, Anda harus memuat Player
dan
MediaSession
di dalam Service yang terpisah.
Hal ini memungkinkan perangkat untuk terus menayangkan media meskipun aplikasi Anda tidak
di latar depan.
![MediaSessionService memungkinkan sesi media berjalan secara terpisah
dari aktivitas aplikasi](https://developer.android.com/static/guide/topics/media/images/mediasessionservice.png?authuser=6&hl=id)
MediaSessionService
memungkinkan sesi
media berjalan terpisah dari aktivitas aplikasiSaat menghosting pemain di dalam Layanan, Anda harus menggunakan MediaSessionService
.
Untuk melakukannya, buat class yang memperluas MediaSessionService
` dan buat
sesi media di dalamnya.
Penggunaan MediaSessionService
memungkinkan klien eksternal seperti Asisten
Google, kontrol media sistem, atau perangkat pendamping seperti Wear OS dapat menemukan
layanan Anda, terhubung ke layanan tersebut, dan mengontrol pemutaran, semuanya tanpa perlu mengakses
aktivitas UI aplikasi Anda sama sekali. Bahkan, mungkin saja ada beberapa aplikasi klien yang terhubung
ke MediaSessionService
yang sama secara bersamaan, dan setiap aplikasi memiliki
MediaController
-nya sendiri.
Mengimplementasikan siklus proses layanan
Anda harus menerapkan tiga metode siklus proses untuk layanan Anda:
onCreate()
dipanggil saat pengontrol pertama akan terhubung dan layanan dibuat instance dan dimulai. Rendering buffer depan ini merupakan tempat terbaik untuk membuatPlayer
danMediaSession
.onTaskRemoved(Intent)
dipanggil saat pengguna menutup aplikasi dari tugas terbaru. Jika pemutaran sedang berlangsung, aplikasi dapat memilih untuk mempertahankan layanan berjalan di latar depan. Jika pemutar dijeda, layanan tidak akan berada di latar depan dan harus dihentikan.onDestroy()
dipanggil saat layanan dihentikan. Semua resource, termasuk pemain dan sesi, harus dirilis.
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(); } }
Sebagai alternatif agar pemutaran tetap berjalan di latar belakang, aplikasi dapat menghentikan layanan dalam kasus apa pun saat pengguna menutup aplikasi:
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(); }
Memberikan akses ke sesi media
Ganti metode onGetSession()
untuk memberi klien lain akses ke sesi
media Anda yang dibuat saat layanan dibuat.
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; } }
Mendeklarasikan layanan dalam manifes
Aplikasi memerlukan izin untuk menjalankan layanan latar depan. Tambahkan
izin FOREGROUND_SERVICE
ke manifes, dan jika Anda menargetkan API 34 dan
yang lebih tinggi juga FOREGROUND_SERVICE_MEDIA_PLAYBACK
:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
Anda juga harus mendeklarasikan class Service
dalam manifes dengan filter intent
MediaSessionService
.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
Anda harus menentukan
foregroundServiceType
yang menyertakan mediaPlayback
saat aplikasi berjalan di perangkat dengan Android
10 (API level 29) dan yang lebih tinggi.
Mengontrol pemutaran menggunakan MediaController
Di Aktivitas atau Fragmen yang berisi UI pemutar, Anda dapat membuat link
antara UI dan sesi media menggunakan MediaController
. UI Anda menggunakan
pengontrol media untuk mengirim perintah dari UI ke pemutar dalam
sesi. Lihat panduan
Membuat MediaController
untuk mengetahui detail cara membuat dan menggunakan MediaController
.
Menangani perintah UI
MediaSession
menerima perintah dari pengontrol melalui
MediaSession.Callback
-nya. Melakukan inisialisasi MediaSession
akan membuat implementasi
default MediaSession.Callback
yang otomatis menangani semua
perintah yang dikirim MediaController
ke pemutar Anda.
Notifikasi
MediaSessionService
akan otomatis membuat MediaNotification
untuk Anda yang
seharusnya berfungsi dalam sebagian besar kasus. Secara default, notifikasi yang dipublikasikan adalah
notifikasi MediaStyle
yang terus mendapatkan informasi terbaru
dari sesi media Anda dan menampilkan kontrol pemutaran. MediaNotification
mengetahui sesi Anda dan dapat digunakan untuk mengontrol pemutaran untuk aplikasi lain
yang terhubung ke sesi yang sama.
Misalnya, aplikasi streaming musik yang menggunakan MediaSessionService
akan membuat
MediaNotification
yang menampilkan judul, artis, dan sampul album untuk
item media saat ini yang diputar bersama kontrol pemutaran berdasarkan konfigurasi
MediaSession
Anda.
Metadata yang diperlukan dapat disediakan dalam media atau dideklarasikan sebagai bagian dari item media seperti dalam cuplikan berikut:
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();
Aplikasi dapat menyesuaikan tombol perintah kontrol Media Android. Baca selengkapnya tentang menyesuaikan kontrol Media Android.
Penyesuaian notifikasi
Untuk menyesuaikan notifikasi, buat
MediaNotification.Provider
dengan DefaultMediaNotificationProvider.Builder
atau dengan membuat implementasi kustom antarmuka penyedia. Tambahkan
penyedia Anda ke MediaSessionService
dengan
setMediaNotificationProvider
.
Pelanjutan pemutaran
Tombol media adalah tombol hardware yang ditemukan di perangkat Android dan perangkat periferal lainnya, seperti tombol putar atau jeda di headset Bluetooth. Media3 menangani input tombol media saat layanan berjalan.
Mendeklarasikan penerima tombol media Media3
Media3 menyertakan API yang memungkinkan pengguna melanjutkan
pemutaran setelah aplikasi dihentikan dan bahkan setelah perangkat
dimulai ulang. Secara default, kelanjutan pemutaran dinonaktifkan. Ini berarti pengguna
tidak dapat melanjutkan pemutaran saat layanan Anda tidak berjalan. Untuk ikut serta, mulailah dengan
mendeklarasikan MediaButtonReceiver
dalam manifes Anda:
<receiver android:name="androidx.media3.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
Menerapkan callback pelanjutan pemutaran
Jika kelanjutan pemutaran diminta oleh perangkat Bluetooth atau
fitur melanjutkan UI Sistem Android,
metode callback onPlaybackResumption()
akan dipanggil.
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; }
Jika Anda telah menyimpan parameter lain seperti kecepatan pemutaran, mode pengulangan, atau
mode acak, onPlaybackResumption()
adalah tempat yang tepat untuk mengonfigurasi pemutar
dengan parameter ini sebelum Media3 menyiapkan pemutar dan memulai pemutaran ketika
callback selesai.
Konfigurasi pengontrol lanjutan dan kompatibilitas mundur
Skenario umum adalah menggunakan MediaController
di UI aplikasi untuk mengontrol
pemutaran dan menampilkan playlist. Pada saat yang sama, sesi ditampilkan
ke klien eksternal seperti kontrol media Android dan Asisten di perangkat seluler atau TV,
Wear OS untuk smartwatch, dan Android Auto di mobil. Aplikasi demo sesi Media3 adalah contoh aplikasi yang menerapkan skenario tersebut.
Klien eksternal ini dapat menggunakan API seperti MediaControllerCompat
dari library AndroidX
lama atau android.media.session.MediaController
framework
Android. Media3 sepenuhnya kompatibel dengan library lama dan
menyediakan interoperabilitas dengan API framework Android.
Menggunakan pengontrol notifikasi media
Penting untuk dipahami bahwa pengontrol lama atau framework ini membaca
nilai yang sama dari PlaybackState.getActions()
dan
PlaybackState.getCustomActions()
framework. Untuk menentukan tindakan dan tindakan kustom
sesi framework, aplikasi dapat menggunakan pengontrol notifikasi media
dan menyetel perintah yang tersedia serta tata letak kustom. Layanan ini menghubungkan pengontrol notifikasi
media ke sesi Anda dan sesi tersebut menggunakan
ConnectionResult
yang ditampilkan oleh onConnect()
callback untuk mengonfigurasi
tindakan dan tindakan kustom sesi framework.
Dengan skenario khusus seluler, aplikasi dapat menyediakan implementasi
MediaSession.Callback.onConnect()
untuk menetapkan perintah dan
tata letak kustom yang tersedia secara khusus untuk sesi framework seperti berikut:
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(); }
Otorisasi Android Auto untuk mengirim perintah kustom
Saat menggunakan MediaLibraryService
dan untuk mendukung Android Auto dengan aplikasi seluler, pengontrol Android Auto
memerlukan perintah yang tersedia dan sesuai. Jika tidak, Media3 akan menolak
perintah kustom yang masuk dari pengontrol tersebut:
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(); }
Aplikasi demo sesi memiliki modul otomotif, yang menunjukkan dukungan untuk Automotive OS yang memerlukan APK terpisah.