音乐播放对接状态栏MediaSession控制
音乐播放对接状态栏MediaSession控制
MediaPlayer播放音源
MediaPlayer
可以播放视频,也可以播放视频,下面是一个使用MediaPlayer
播放音频的示例
fun playMp3FromUrl(url: String) {
val mediaPlayer = MediaPlayer()
try {
// 1. 设置数据源
mediaPlayer.setDataSource(url)
// 2. 异步准备播放器
mediaPlayer.prepareAsync()
// 3. 监听准备完成事件
mediaPlayer.setOnPreparedListener { mp ->
// 4. 准备完成后开始播放
mp.start()
}
// 5. 监听播放错误
mediaPlayer.setOnErrorListener { mp, what, extra ->
// 处理错误,例如网络问题、文件不存在等
println("播放错误:what=$what, extra=$extra")
false // 返回 false 表示你没有处理完错误,让系统继续处理
}
//6. 监听播放结束
mediaPlayer.setOnCompletionListener { mp->
// 播放结束
println("播放完成")
}
} catch (e: IOException) {
// 处理 IOException,例如无效的 URL
println("IO 错误:${e.message}")
}
}
如果要想实现在通知栏显示以及控制播放,如下图,我们得自己实现自定义通知栏,处理控制事件
如果我们想让下拉状态栏,可以控制音频播放,实现下图效果
我们就需要对接 MediaSession
,可以参考如下代码实现
private lateinit var mediaSession: MediaSession
private fun setupMediaSession(context:Context) {
mediaSession = MediaSession(this, "MusicService")
mediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS or MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS)
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
mediaSession.setAudioAttributes(audioAttributes)
mediaSession.setMediaControllerCallback(object : MediaSession.Callback() {
override fun onPlay() {
// 处理播放逻辑
Toast.makeText(context, "Play action", Toast.LENGTH_SHORT).show()
}
override fun onPause() {
// 处理暂停逻辑
Toast.makeText(context, "Pause action", Toast.LENGTH_SHORT).show()
}
override fun onSkipToNext() {
// 处理下一首逻辑
Toast.makeText(context, "Next song action", Toast.LENGTH_SHORT).show()
}
override fun onSkipToPrevious() {
// 处理上一首逻辑
Toast.makeText(context, "Previous song action", Toast.LENGTH_SHORT).show()
}
})
val mediaMetadata = MediaMetadata.Builder()
.putString(MediaMetadata.METADATA_KEY_TITLE, "Song Title")
.putString(MediaMetadata.METADATA_KEY_ARTIST, "Artist Name")
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null) // 替换为实际专辑封面
.build()
mediaSession.setMetadata(mediaMetadata)
mediaSession.isActive = true
}
是否有其他简单的方案?
是的,使用 androidx.media3
库中的 ExoPlayer
和 MediaSession
能够更方便地对接系统通知栏和状态栏的播放控制。
使用ExoPlayer播放音频
ExoPlayer
是 Google
官方推荐的媒体播放库,它比 MediaPlayer
功能更强大且更灵活。
ExoPlayer
已经被集成到 androidx.media3
库中,方便开发者使用。下面是使用示例:
依赖仓库如下
implementation("androidx.media3:media3-exoplayer:1.6.0")
implementation("androidx.media3:media3-session:1.6.0")
implementation("androidx.media3:media3-ui:1.6.0")
播放代码
fun playMp3WithExoPlayer(context: Context, mp3Url: String) {
// 1. 创建 ExoPlayer 实例
val player = ExoPlayer.Builder(context).build()
// 2. 创建 MediaItem
val mediaItem = MediaItem.fromUri(Uri.parse(mp3Url))
// 3. 将 MediaItem 添加到播放器
player.setMediaItem(mediaItem)
// 4. 准备播放器
player.prepare()
// 5. 开始播放
player.play()
// 在不需要时,释放播放器
// player.release()
}
如何对接系统通知栏和状态栏的播放控制,需要如下修改,设置MediaMetadata
,并使用PlayerNotificationManager
// 实现 MediaLibrarySessionCallback
private class MediaLibrarySessionCallback : MediaLibrarySession.Callback {
override fun onAddMediaItems(
mediaLibrarySession: MediaLibrarySession,
controller: MediaSession.ControllerInfo,
mediaItems: MutableList<MediaItem>
): ListenableFuture<List<MediaItem>> {
// val updatedMediaItems = mediaItems.map { it.buildUpon().setUri(it.requestMetadata.mediaUri!!).build() }
return Futures.immediateFuture(mediaItems)
}
}
//初始化 mediaSession
mediaSession = MediaLibrarySession.Builder( this, player, MediaLibrarySessionCallback()).build()
private fun createPendingIntent(): PendingIntent {
val notificationIntent = Intent(this, HomeActivity::class.java)
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
} else {
PendingIntent.getActivity(
this, 0, notificationIntent, PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
return pendingIntent
}
// 设置通知栏 使用 ExoPlayer 播放
notificationManager = PlayerNotificationManager.Builder(this, NOTIFICATION_ID, CHANNEL_ID)
.setMediaDescriptionAdapter(DefaultMediaDescriptionAdapter(createPendingIntent()))
.setNotificationListener(object : PlayerNotificationManager.NotificationListener {
override fun onNotificationPosted(
notificationId: Int, notification: Notification, ongoing: Boolean
) {
Log.d("onNotificationPosted:,ongoing:$ongoing,notification:$notification")
startForeground(notificationId, notification)
}
override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
Log.d("onNotificationCancelled: ")
}
}).build()
notificationManager?.setSmallIcon(R.mipmap.icon_default_logo)
notificationManager?.setMediaSessionToken(mediaSession.platformToken)
notificationManager?.setPlayer(player)
// 设置MediaMetadata,以便状态栏和通知栏播放控制器使用
// 你的媒体数据类,需要包含 name, content, coverImage, audioUrl, id 等属性
val metaData = MediaMetadata.Builder().setTitle(item.name).setArtist(item.content)
.setArtworkUri(item.coverImage?.toUri()).build()
var mediaItem = MediaItem.Builder()
.setMediaMetadata(metaData).setUri(item.audioUrl)
.build()
player.addMediaItem(mediaItem)
可以通过如下方式播放列表指定的索引
player.seekTo(index, 0)
使用 ExoPlayer
相对 MediaPlayer
简单了许多,因为它提供了更高级别的 API 来处理媒体会话和通知栏控制。
Exopler 缓存配置
播放音频时,我们可以配置缓存,可以在一定程度减少等待时间,减少服务器带宽压力
@OptIn(UnstableApi::class)
private val cacheDataSourceFactory by lazy {
val cacheFolder = File(MyApplication.getInstance().cacheDir, "media_cache")
val databaseProvider = StandaloneDatabaseProvider(MyApplication.getInstance())
val lru = LeastRecentlyUsedCacheEvictor(200 * 1024 * 1024)
val simpleCache = SimpleCache(cacheFolder, lru, databaseProvider)
val httpDataSourceFactory = DefaultHttpDataSource.Factory()
CacheDataSource.Factory().setCache(simpleCache).setUpstreamDataSourceFactory(httpDataSourceFactory)
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
}
val mediaSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory).createMediaSource(mediaItem)
// 列表需要替换 addMediaItem 使用 addMediaSource
player.setMediaSource(mediaSource)
总结
本文介绍了在 Android 应用中对接音视频播放与系统状态栏 MediaSession 控制的两种主要方式:使用 MediaPlayer
和使用 ExoPlayer
。
-
MediaPlayer: 虽然
MediaPlayer
可以实现基本的音频播放,但要对接系统通知栏和状态栏的播放控制,需要开发者手动创建和管理MediaSessionCompat
以及自定义通知栏,处理各种播放控制事件,实现较为复杂。 -
ExoPlayer:
ExoPlayer
是 Google 官方推荐的更强大、更灵活的媒体播放库,并且与androidx.media3
库中的MediaSession
和PlayerNotificationManager
集成得更好。使用ExoPlayer
和PlayerNotificationManager
可以更简洁地实现状态栏和通知栏的播放控制,ExoPlayer
会自动处理大部分与系统媒体控制的交互。
本文链接:音乐播放对接状态栏MediaSession控制 - https://h89.cn/archives/364.html
版权声明:原创文章 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文链接和本声明。