Sesi media memberikan cara universal untuk berinteraksi dengan pemutar audio atau
video. Di Media3, pemutar default adalah class ExoPlayer
, yang mengimplementasikan
antarmuka Player
. Menghubungkan sesi media ke pemutar memungkinkan aplikasi
untuk mengiklankan pemutaran media secara eksternal dan menerima perintah pemutaran dari
sumber eksternal.
Perintah dapat berasal dari tombol fisik seperti tombol putar di headset atau remote control TV. Perintah ini mungkin juga berasal dari aplikasi klien yang memiliki pengontrol media, seperti memerintahkan "jeda" ke Asisten Google. Sesi media mendelegasikan perintah ini ke pemutar aplikasi media.
Kapan harus memilih sesi media
Saat mengimplementasikan MediaSession
, Anda mengizinkan pengguna mengontrol pemutaran:
- Melalui headphone mereka. Sering kali ada tombol atau interaksi sentuh yang dapat dilakukan pengguna di headphone untuk memutar atau menjeda media, atau memutar lagu berikutnya atau sebelumnya.
- Dengan berbicara ke Asisten Google. Pola yang umum adalah mengucapkan "Ok Google, jeda" untuk menjeda media apa pun yang sedang diputar di perangkat.
- Melalui smartwatch Wear OS. Hal ini memungkinkan akses yang lebih mudah ke kontrol pemutaran yang paling umum saat bermain di ponsel.
- Melalui Kontrol media. Carousel ini menampilkan kontrol untuk setiap sesi media yang berjalan.
- Di TV. Mengizinkan tindakan dengan tombol pemutaran fisik, kontrol pemutaran platform, dan pengelolaan daya (misalnya jika TV, soundbar, atau penerima A/V nonaktif atau input dialihkan, pemutaran akan berhenti di aplikasi).
- Serta proses eksternal lainnya yang perlu memengaruhi pemutaran.
Ini bagus untuk banyak kasus penggunaan. Secara khusus, Anda harus sangat mempertimbangkan
penggunaan MediaSession
saat:
- Anda melakukan streaming konten video panjang, seperti film atau TV live.
- Anda melakukan streaming konten audio berdurasi panjang, seperti podcast atau playlist musik.
- Anda sedang membuat aplikasi TV.
Namun, tidak semua kasus penggunaan cocok dengan MediaSession
. Anda mungkin hanya perlu menggunakan Player
dalam kasus berikut:
- Anda menampilkan konten video pendek, di mana engagement dan interaksi pengguna sangat penting.
- Tidak ada satu video aktif, seperti pengguna men-scroll daftar dan beberapa video ditampilkan di layar secara bersamaan.
- Anda memutar video pengantar atau penjelasan satu kali, yang diharapkan akan ditonton secara aktif oleh pengguna.
- Konten Anda sensitif privasi dan Anda tidak ingin proses eksternal mengakses metadata media (misalnya mode samaran di browser)
Jika kasus penggunaan Anda tidak sesuai dengan salah satu yang tercantum di atas, pertimbangkan apakah Anda
akan mengizinkan aplikasi melanjutkan pemutaran saat pengguna tidak berinteraksi secara aktif
dengan konten tersebut. Jika jawabannya ya, Anda mungkin memilih
MediaSession
. Jika jawabannya tidak, Anda mungkin ingin menggunakan
Player
sebagai gantinya.
Membuat sesi media
Sesi media berdampingan dengan pemutar yang dikelolanya. Anda dapat membuat
sesi media dengan objek Context
dan Player
. Anda harus membuat dan
melakukan inisialisasi sesi media jika diperlukan, seperti metode siklus proses onStart()
atau
onResume()
dari metode Activity
atau Fragment
, atau onCreate()
dari Service
yang memiliki sesi media dan pemutar terkaitnya.
Untuk membuat sesi media, lakukan inisialisasi Player
dan berikan ke
MediaSession.Builder
seperti ini:
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Penanganan status otomatis
Library Media3 otomatis memperbarui sesi media menggunakan status pemutar. Dengan demikian, Anda tidak perlu menangani pemetaan secara manual dari pemain ke sesi.
Hal ini merupakan jeda dari pendekatan lama yang mengharuskan Anda membuat dan mengelola
PlaybackStateCompat
secara terpisah dari pemutar itu sendiri, misalnya untuk
menunjukkan error.
ID sesi unik
Secara default, MediaSession.Builder
membuat sesi dengan string kosong sebagai ID sesi. Tindakan ini sudah cukup jika aplikasi hanya bermaksud membuat satu
instance sesi, yang merupakan kasus yang paling umum.
Jika ingin mengelola beberapa instance sesi secara bersamaan, aplikasi
harus memastikan bahwa ID sesi setiap sesi bersifat unik. ID sesi dapat
ditetapkan saat mem-build sesi dengan MediaSession.Builder.setId(String id)
.
Jika Anda melihat IllegalStateException
membuat aplikasi error dengan pesan
error IllegalStateException: Session ID must be unique. ID=
, kemungkinan
sesi telah dibuat secara tidak terduga sebelum instance yang dibuat sebelumnya
dengan ID yang sama dirilis. Untuk menghindari sesi dibocorkan oleh
error pemrograman, kasus tersebut akan terdeteksi dan diberi tahu dengan menampilkan
pengecualian.
Memberikan kontrol ke klien lain
Sesi media adalah kunci untuk mengontrol pemutaran. Dengan API ini, Anda dapat merutekan perintah dari sumber eksternal ke pemutar yang bertugas memutar media Anda. Sumber ini dapat berupa tombol fisik seperti tombol putar di headset atau remote control TV, atau perintah tidak langsung seperti memerintahkan "jeda" ke Asisten Google. Demikian pula, Anda mungkin ingin memberikan akses ke sistem Android untuk memfasilitasi notifikasi dan kontrol layar kunci, atau ke smartwatch Wear OS sehingga Anda dapat mengontrol pemutaran dari tampilan jam. Klien eksternal dapat menggunakan pengontrol media untuk mengeluarkan perintah pemutaran ke aplikasi media Anda. Klien ini diterima oleh sesi media Anda, yang pada akhirnya mendelegasikan perintah ke pemutar media.
Saat pengontrol akan terhubung ke sesi media Anda, metode
onConnect()
dipanggil. Anda dapat menggunakan ControllerInfo
yang disediakan untuk memutuskan apakah akan menerima
atau menolak
permintaan. Lihat contoh penerimaan permintaan koneksi di bagian Mendeklarasikan perintah yang tersedia.
Setelah terhubung, pengontrol dapat mengirimkan perintah pemutaran ke sesi. Sesi
kemudian mendelegasikan perintah tersebut ke pemain. Perintah pemutaran dan playlist
yang ditentukan dalam antarmuka Player
otomatis ditangani oleh
sesi.
Metode callback lainnya memungkinkan Anda menangani, misalnya, permintaan untuk
perintah pemutaran kustom dan
mengubah playlist).
Callback ini juga menyertakan objek ControllerInfo
sehingga Anda dapat mengubah
cara Anda merespons setiap permintaan berdasarkan setiap pengontrol.
Mengubah playlist
Sesi media dapat langsung memodifikasi playlist pemutarnya seperti yang dijelaskan dalam
panduan ExoPlayer untuk playlist.
Pengontrol juga dapat mengubah playlist jika
COMMAND_SET_MEDIA_ITEM
atau COMMAND_CHANGE_MEDIA_ITEMS
tersedia untuk pengontrol.
Saat menambahkan item baru ke playlist, pemutar biasanya memerlukan instance MediaItem
dengan
URI yang ditentukan
agar dapat diputar. Secara default, item yang baru ditambahkan akan otomatis diteruskan
ke metode pemutar seperti player.addMediaItem
jika item tersebut memiliki URI yang ditentukan.
Jika ingin menyesuaikan instance MediaItem
yang ditambahkan ke pemutar, Anda dapat
mengganti
onAddMediaItems()
.
Langkah ini diperlukan saat Anda ingin mendukung pengontrol yang meminta media
tanpa URI yang ditentukan. Sebagai gantinya, MediaItem
biasanya memiliki
satu atau beberapa kolom berikut yang ditetapkan untuk mendeskripsikan media yang diminta:
MediaItem.id
: ID umum yang mengidentifikasi media.MediaItem.RequestMetadata.mediaUri
: URI permintaan yang dapat menggunakan skema kustom dan tidak harus dapat diputar langsung oleh pemain.MediaItem.RequestMetadata.searchQuery
: Kueri penelusuran tekstual, misalnya dari Asisten Google.MediaItem.MediaMetadata
: Metadata terstruktur seperti 'judul' atau 'artis'.
Untuk opsi penyesuaian lainnya terkait playlist yang benar-benar baru, Anda juga dapat
mengganti
onSetMediaItems()
yang memungkinkan Anda menentukan item awal dan posisi dalam playlist. Misalnya,
Anda dapat memperluas satu item yang diminta ke seluruh playlist dan menginstruksikan
pemutar untuk memulai indeks item yang awalnya diminta. Contoh penerapan onSetMediaItems()
dengan fitur ini dapat ditemukan di aplikasi demo sesi.
Mengelola tata letak khusus dan perintah khusus
Bagian berikut menjelaskan cara memberitahukan tata letak kustom tombol perintah kustom kepada aplikasi klien dan memberi otorisasi kepada pengontrol untuk mengirim perintah kustom.
Menentukan tata letak kustom sesi
Untuk menunjukkan kepada aplikasi klien kontrol pemutaran mana yang ingin Anda tampilkan kepada
pengguna, setel tata letak kustom sesi
saat mem-build MediaSession
dalam metode onCreate()
layanan
Anda.
Kotlin
override fun onCreate() { super.onCreate() val likeButton = CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(SessionCommand(SAVE_TO_FAVORITES, Bundle())) .build() session = MediaSession.Builder(this, player) .setCallback(CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build() }
Java
@Override public void onCreate() { super.onCreate(); CommandButton likeButton = new CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); Player player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player) .setCallback(new CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build(); }
Mendeklarasikan pemutar yang tersedia dan perintah kustom
Aplikasi media dapat menentukan perintah kustom yang, misalnya, dapat digunakan dalam
tata letak kustom. Misalnya, Anda mungkin ingin mengimplementasikan tombol yang memungkinkan
pengguna menyimpan item media ke daftar item favorit. MediaController
mengirimkan perintah kustom dan MediaSession.Callback
menerimanya.
Anda dapat menentukan perintah sesi kustom yang tersedia untuk
MediaController
saat terhubung ke sesi media. Anda dapat mencapainya dengan
mengganti MediaSession.Callback.onConnect()
. Konfigurasikan dan tampilkan
kumpulan perintah yang tersedia saat menerima permintaan koneksi dari
MediaController
dalam metode callback onConnect
:
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): MediaSession.ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ConnectionResult onConnect( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } }
Untuk menerima permintaan perintah kustom dari MediaController
, ganti metode
onCustomCommand()
di Callback
.
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { ... override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture<SessionResult> { if (customCommand.customAction == SAVE_TO_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture( SessionResult(SessionResult.RESULT_SUCCESS) ) } ... } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { ... @Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args ) { if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture( new SessionResult(SessionResult.RESULT_SUCCESS) ); } ... } }
Anda dapat melacak pengontrol media mana yang membuat permintaan menggunakan
properti packageName
dari objek MediaSession.ControllerInfo
yang
diteruskan ke metode Callback
. Hal ini memungkinkan Anda menyesuaikan perilaku aplikasi
sebagai respons terhadap perintah tertentu jika berasal dari sistem, aplikasi
Anda sendiri, atau aplikasi klien lainnya.
Memperbarui tata letak kustom setelah interaksi pengguna
Setelah menangani perintah kustom atau interaksi lainnya dengan pemutar, Anda
mungkin perlu memperbarui tata letak yang ditampilkan di UI pengontrol. Contoh umumnya
adalah tombol yang mengubah ikonnya setelah memicu tindakan yang terkait
dengan tombol ini. Untuk memperbarui tata letak, Anda dapat menggunakan
MediaSession.setCustomLayout
:
Kotlin
val removeFromFavoritesButton = CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(SessionCommand(REMOVE_FROM_FAVORITES, Bundle())) .build() mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
CommandButton removeFromFavoritesButton = new CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(new SessionCommand(REMOVE_FROM_FAVORITES, new Bundle())) .build(); mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton));
Menyesuaikan perilaku perintah pemutaran
Untuk menyesuaikan perilaku perintah yang ditentukan dalam antarmuka Player
, seperti
play()
atau seekToNext()
, gabungkan Player
Anda dalam ForwardingPlayer
.
Kotlin
val player = ExoPlayer.Builder(context).build() val forwardingPlayer = object : ForwardingPlayer(player) { override fun play() { // Add custom logic super.play() } override fun setPlayWhenReady(playWhenReady: Boolean) { // Add custom logic super.setPlayWhenReady(playWhenReady) } } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player) { @Override public void play() { // Add custom logic super.play(); } @Override public void setPlayWhenReady(boolean playWhenReady) { // Add custom logic super.setPlayWhenReady(playWhenReady); } }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
Untuk mengetahui informasi selengkapnya tentang ForwardingPlayer
, lihat panduan ExoPlayer tentang
Penyesuaian.
Mengidentifikasi pengontrol permintaan dari perintah pemutar
Saat panggilan ke metode Player
berasal dari MediaController
, Anda dapat
mengidentifikasi sumber asal dengan MediaSession.controllerForCurrentRequest
dan mendapatkan ControllerInfo
untuk permintaan saat ini:
Kotlin
class CallerAwareForwardingPlayer(player: Player) : ForwardingPlayer(player) { override fun seekToNext() { Log.d( "caller", "seekToNext called from package ${session.controllerForCurrentRequest?.packageName}" ) super.seekToNext() } }
Java
public class CallerAwareForwardingPlayer extends ForwardingPlayer { public CallerAwareForwardingPlayer(Player player) { super(player); } @Override public void seekToNext() { Log.d( "caller", "seekToNext called from package: " + session.getControllerForCurrentRequest().getPackageName()); super.seekToNext(); } }
Merespons tombol media
Tombol media adalah tombol hardware yang ditemukan di perangkat Android dan perangkat periferal
lainnya, seperti tombol putar/jeda pada headset Bluetooth. Media3 menangani
peristiwa tombol media untuk Anda saat tiba di sesi dan memanggil
metode Player
yang sesuai pada pemutar sesi.
Aplikasi dapat mengganti perilaku default dengan mengganti
MediaSession.Callback.onMediaButtonEvent(Intent)
. Dalam kasus semacam itu, aplikasi
dapat/perlu menangani semua spesifikasi API sendiri.