Jetpack - Media3(MediaController 响应外部控制) 一、概念二、提供服务2.1 创建 ServiceMediaSession.Callback可重写的回调onAddMediaItems()onConnect()onCustomCommand()控制器发送过来的自定义命令。onDisconnected()onMediaButtonEvent()onPlaybackResumption()onPlayerInteractionFinished()onPostConnect()onSetMediaItems()onSetRating()class PlayerService : MediaSessionService() { private var mediaSession: MediaSession? null private val sessionCallback by lazy { //重写需要的回调来响应外部控制器发送的命令 object : MediaSession.Callback {} } override fun onCreate() { super.onCreate() //不建议在这里设置播放器监听Player.Listener //因为可能有很多MediaController连接过来 //所以应该放在初始化MediaController的时候定制各自的监听 val player ExoPlayer.Builder(this).build() mediaSession MediaSession.Builder(this, player) .setCallback(sessionCallback) //设置会话监听 .build() } // 供外部控制器访问 override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? { return mediaSession } // 用户关闭应用时停止服务 override fun onTaskRemoved(rootIntent: Intent?) { pauseAllPlayersAndStopSelf() } // 释放资源 override fun onDestroy() { super.onDestroy() mediaSession?.run { player.release() release() mediaSession null } } }2.2 注册 Serviceservice android:name.PlayerService android:foregroundServiceTypemediaPlayback android:exportedtrue intent-filter action android:nameandroidx.media3.session.MediaSessionService/ action android:nameandroid.media.browse.MediaBrowserService/ /intent-filter /service2.3 配置权限uses-permission android:nameandroid.permission.FOREGROUND_SERVICE / uses-permission android:nameandroid.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK /三、使用3.1 连接服务class ControllerActivity : AppCompatActivity() { private lateinit var controllerFuture: ListenableFutureMediaController private var mediaController: MediaController? null private val controllerListener by lazy { //重写需要的控制器监听 object : MediaController.Listener {} } private val playerListener by lazy { //重写需要的播放器监听 object : Player.Listener {} } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_controller) startForegroundService(Intent(this, PlayerService::class.java)) connectToSession() } private fun connectToSession() { // 指定要连接的Service val sessionToken SessionToken(this, ComponentName(this, PlayerService::class.java)) // 异步构建 MediaController controllerFuture MediaController.Builder(this, sessionToken) .setListener(controllerListener) //控制器监听 .buildAsync() // 连接成功的回调 controllerFuture.addListener({ try { mediaController controllerFuture.get().apply{ // 进行初始化配置用起来同ExoPlayer的Player一样 addListener(playerListener) //播放器监听 repeatMode Player.REPEAT_MODE_ONE prepare() } } catch (e: Exception) { //连接失败 } }, mainExecutor ) } override fun onDestroy() { //释放资源 MediaController.releaseFuture(controllerFuture) } }3.1.1 优化mediaController 不一定什么时候初始化完毕若是需要界面一打开就要播放的情况可以对成员变量使用Delegates在不为默认值null时撤掉遮挡UI允许用户操作。private var mediaController: MediaController? by Delegates.observable(null) { property, oldValue, newValue - //此时 mediaController 可用关闭初始化遮挡界面允许用户操作 }3.2 设置控制器监听连接监听MediaController.Listener控制器可以设置的监听onAvailableSessionCommandsChanged()onCustomCommand()会话发送过来的自定义命令。onCustomLayoutChanged()onDisconnected()onError()onExtrasChanged()onMediaButtonPreferencesChanged()onSessionActivityChanged()onSetCustomLayout()private val controllerListener by lazy { object : MediaController.Listener { // 当会话发送自定义命令时 override fun onCustomCommand(controller: MediaController, command: SessionCommand, args: Bundle): ListenableFutureSessionResult { return when (command.customAction) { // FAVORITE - { // val isFavorite args.getBoolean(isFavorite, false) // handleFavoriteCommand(isFavorite) // Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) // } else - super.onCustomCommand(controller, command, args) } } // 当控制器与会话断开连接时 override fun onDisconnected(controller: MediaController) { super.onDisconnected(controller) } } }3.3 设置播放器监听同 ExoPlayer都是 Player.Listener。private val playerListener by lazy { object : Player.Listener { //播放器状态 override fun onPlaybackStateChanged(playbackState: Int) { when (playbackState) { //空闲 Player.STATE_IDLE - {} //缓冲中 Player.STATE_BUFFERING - {} //就绪 Player.STATE_READY - {} //播放完 Player.STATE_ENDED - {} } } //播放列表 override fun onTimelineChanged(timeline: Timeline, reason: Int) { when(reason) { //播放列表发生变化时 Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED - {} //媒体源更新而发生变化 Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE - {} } } //播放错误 override fun onPlayerError(error: PlaybackException) { when (error.cause) { //http错误 is HttpDataSource.HttpDataSourceException - {} else - {} } when (error.errorCode) { //网络错误 PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED - {} //解码错误 PlaybackException.ERROR_CODE_DECODING_FAILED - {} else - {} } } //播放暂停按钮 override fun onIsPlayingChanged(isPlaying: Boolean) {} //切歌 override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {} } }

相关新闻

最新新闻

DeepSeek V4 Flash 接入 Codex:配置部署与 skill 插件实践

DeepSeek V4 Flash 接入 Codex:配置部署与 skill 插件实践

这次我们来看一个最近讨论度非常高的话题:DeepSeek V4 Flash 和 Codex 的组合玩法。标题里的“DeepSeek Codex 王炸”,并不是把两个产品简单装在一起,而是把 DeepSeek 的模型能力接入 Codex 客户端,再通过 skill 和插件扩展它的…

2026/8/31 8:19:49
基于STM32的智能风扇控制系统设计与实现|毕设答辩|单片机项目|毕设

基于STM32的智能风扇控制系统设计与实现|毕设答辩|单片机项目|毕设

一、项目介绍 摘 要 为解决传统风扇控制方式单一、能源浪费严重、智能化程度低的问题,本文设计并实现了一套基于STM32F411CEU6的智能风扇控制系统。该系统以STM32F411CEU6单片机为控制核心,选用SHT30数字温湿度传感器采集环境温湿度,HC-SR50…

2026/8/31 8:19:49
obsidian-skills 的 5 个技能:让 AI 代理写出规范的 Obsidian 文件

obsidian-skills 的 5 个技能:让 AI 代理写出规范的 Obsidian 文件

obsidian-skills 的 5 个技能:让 AI 代理写出规范的 Obsidian 文件 【免费下载链接】obsidian-skills Agent skills for Obsidian. Teach your agent to use Obsidian CLI and open formats including Markdown, Bases, JSON Canvas. 项目地址: https://gitcode.c…

2026/8/31 8:19:49
掼蛋7分牌怎么打?首发牌选择与出牌权控制策略

掼蛋7分牌怎么打?首发牌选择与出牌权控制策略

各位牌友,大家好。不知道你有没有遇到过这样的局面:起手牌型非常整齐,牌力也足够强,单双顺不缺,炸弹也有,按理说是稳稳的一手好牌。结果首发一冲,不但没能掌握节奏,反而被对手连续顶…

2026/8/31 8:19:49
思源笔记 v3.4:看板视图上线,把数据库变成能拖的项目看板

思源笔记 v3.4:看板视图上线,把数据库变成能拖的项目看板

思源笔记 v3.4:看板视图上线,把数据库变成能拖的项目看板 【免费下载链接】siyuan An open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与…

2026/8/31 8:19:48
Java后端面试一周冲刺:查漏补缺与高效备战指南

Java后端面试一周冲刺:查漏补缺与高效备战指南

有朋友问,如果真按“8月11日到18日”这种一周倒计时去准备Java后端面试,还能不能冲一把。我的回答是:可以,但别把“一周刷透大厂必考100题”理解成“背完一百题就能拿offer”,更合理的理解是“用一周时间,把…

2026/8/31 8:14:48