本系列的前一篇文章介绍了通过连接MediaSources创建播放列表所需的步骤,以及定制ExoPlayer播放控制UI的详细信息,本文详细介绍了如何使用MediaSession连接器扩展在应用程序中无缝使用MediaSession功能。
MediaSession介绍
MediaSession允许播放器中的信息暴露给Android系统的其他应用。
- 例如播放器当前是暂停,播放还是加载文件等,都可以通过MediaSession广播当操作系统的其他应用程序或组件中。
- 其他应用程序或操作系统也可以通过绑定到MediaSession的Mediacontroller.TransportControls来操作播放器。这允许蓝牙耳机上的媒体按钮,Google智能助理甚至其他应用能够播放,暂停或跳过媒体应用中的歌曲。
总之,MediaSession有两个用途,发布有关您的应用程序中正在播放的当前媒体的元数据,并允许其他组件控制播放。
元数据可以被蓝牙设备,Android可穿戴设备以及Google智能助理使用。除了操作系统,播放控制与元数据使用类似(都可以被有线耳机的按钮转换为可以被你使用的命令)。
要想了解更多的MediaSession资源,如下所示:
- Understanding MediaSession medium article
- Working with a MediaSession on d.android.com
- Android sample code for working with MediaSession
使用MediaSession连接器扩展
确保添加如下配置到build.gradle文件中。
ext { ver = “2.7.0” }
dependencies {
implementation
“com.google.android.exoplayer:extension-mediasession:$ver”
}
然后你可以创建session然后attach到播放器中。
override fun onCreate(savedInstanceState: Bundle?){
...
playerHolder = PlayerHolder(this, exoplayerview_activity_video, state)
mediaSession = MediaSessionCompat(this, packageName)
connector = MediaSessionConnector(mediaSession)
}
override fun onStart(){
...
playerHolder.start()
connector.setPlayer(playerHolder.player, null)
mediaSession.isActive = true
}
override fun onStop() {
...
playerHolder.stop()
connector.setPlayer(null, null)
mediaSession.isActive = false
}
override fun onDestroy() {
...
mediaSession.release()
playerHolder.release()
}
关于java可变参数和Kotlin的注意事项 - ExoPlayer和MediaSession连接器扩展用Java编写,并且setPlayer()方法接受可变参数作为第三个参数。如果但你从Kotlin调用这个方法时,如果你们有传递第三个参数,那么所有的东西都可以像使用Java那样,但是如果你传入了null作为第三个参数,那么将抛出空指针异常,如果你确实需要一个参数列表,那么你必须使用spread运算符来将参数列表传递给java可变参数,如下是示例:
val uriList = mutableListOf<MediaSource>()
val mediaSource = ConcatenatingMediaSource(*uriList.toTypedArray())
这样做将启用基本的播放操作(ACTION_PLAY_PAUSE, ACTION_PLAY, ACTION_PAUSE, ACTION_SEEK_TO, ACTION_FAST_FORWARD, ACTION_REWIND, ACTION_STOP),这意味着连机器能够处理通过MediaSession生成的这些操作,例如蓝牙耳机可以控制暂停,播放,快进和快退的操作等。
但是,如果你想要控制播放列表,则还必须通过添加QueueNavigator来告诉连接器处理**ACTION_SKIP_ ***相关的命令,下面是示例:
override fun onCreate(savedInstanceState: Bundle?) {
...
mediaSession = MediaSessionCompat(this, packageName)
connector = MediaSessionConnector(mediaSession)
// If QueueNavigator isn't set, then mediaSessionConnector won’t handle
// MediaSession actions (they won't show up in the minimized PIP activity):
// [ACTION_SKIP_PREVIOUS], [ACTION_SKIP_NEXT], [ACTION_SKIP_TO_QUEUE_ITEM]
connector.setQueueNavigator(object : TimelineQueueNavigator(mMediaSession) {
override fun getMediaDescription(idx: Int): MediaDescriptionCompat {
return MediaCatalog.list.get(idx)
}
})
}
object MediaCatalog {
val list = mutableListOf<MediaDescriptionCompat>()
init {
list.add(
with(MediaDescriptionCompat.Builder()) {
setDescription("MP4 loaded from assets folder")
setMediaId("1")
setMediaUri(Uri.parse("asset:///video/...video.mp4"))
setTitle("Stock footage")
setSubtitle("Local video")
build()
})
list.add(
with(MediaDescriptionCompat.Builder()) {
setDescription("MP3 loaded over HTTP")
setMediaId("2")
setMediaUri(Uri.parse("http://storage.../play.mp3"))
setTitle("Spoken track")
setSubtitle("Streaming audio")
build()
})
list.add(...)
}
}
使用此示例,您还可以更改创建ExoPlayer及其播放列表的代码,以便从该相同的MediaCatalog列表对象加载播放列表,加载播放列表的代码现在看起来这样:
player = ExoPlayerFactory.newSimpleInstance(context, DefaultTrackSelector())
.apply {
...
}
fun buildMediaSource(): MediaSource {
val uriList = mutableListOf<MediaSource>()
MediaCatalog.list.forEach {
uriList.add(createExtractorMediaSource(it.mediaUri!!))
}
return ConcatenatingMediaSource(*uriList.toTypedArray())
}
private fun createExtractorMediaSource(uri: Uri): MediaSource {
return ExtractorMediaSource.Factory(
DefaultDataSourceFactory(context, "videoapp")).createMediaSource(uri)
}
除了上面描述的之外,您还可以告诉连接器您想要处理其他MediaSession操作,你可以在这篇文章.
中找到关于以下每一项的更多信息。
-
MediaSessionConnector.PlaybackPreparer - 这允许您处理ACTION_PREPARE_FROM_SEARCH和ACTION_PREPARE_FROM_URI等操作,如果您想要与Android Auto或Google智能助理集成,则需要执行此操作。有关允许Google智能助理通过MediaSession控制媒体应用的更多信息,请参阅developers.android.com.
上的这篇文章。 - MediaSessionConnector.PlaybackController - 这使您可以处理基本的播放控制器操作,例如ACTION_PLAY_PAUSE和ACTION_SEEK_TO。这是可选的,如果你不用这个拦截这样的调用,就使用DefaultPlaybackController。
- MediaSessionConnector.QueueEditor - 这允许您处理ACTION_SET_RATING和其他MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMAND命令。 TimelineQueueEditor是一个与DynamicConcatenatingMediaSource一起工作的实现。
- MediaSessionConnector.CustomActionProvider - 处理您想要在您的应用中提供的任何自定义操作,例如提供一种方法来控制应用中的repeat mode。