Android MediaSession 与 Media 框架超详细指南:从原理到实战

21 阅读4分钟

一、MediaSession:跨设备媒体控制的 “中央枢纽”

一句话理解:MediaSession 是 Android 系统为媒体应用提供的 “智能管家”,它统一管理播放状态(如播放 / 暂停)、接收跨设备控制指令(如耳机、车载系统),并将信息同步到通知栏、锁屏等界面。

🌰 生活类比:家庭智能音响的遥控器
  • 你在家用手机播放音乐,此时:

    • 手机屏幕、智能手表、蓝牙音箱按钮都能控制暂停 —— 这相当于 MediaController 发送指令;
    • 音响内部记录 “当前播放第 3 首歌、进度 50%”—— 这是 MediaSession 管理的状态;
    • 音响后台持续运行并提供歌曲列表 —— 这是 MediaBrowserService 在工作。

二、Media 框架核心组件:四大角色分工

1. MediaSession(媒体会话):状态管理中枢
  • 核心职责

    • 存储播放状态(播放中 / 暂停)、媒体信息(歌名、封面、进度);
    • 接收控制指令(播放 / 暂停 / 切歌),并通知播放器执行;
    • 同步状态到系统(如通知栏、锁屏界面)。
  • 关键代码示例

    kotlin

    // 初始化 MediaSession
    mediaSession = MediaSessionCompat(this, "MusicApp")
    // 设置媒体信息(如歌曲封面、标题)
    mediaSession.setMetadata(
        MediaMetadataCompat.Builder()
            .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "七里香")
            .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt)
            .build()
    )
    // 设置播放状态(播放中,进度15秒)
    mediaSession.setPlaybackState(
        PlaybackStateCompat.Builder()
            .setState(PlaybackStateCompat.STATE_PLAYING, 15000, 1.0f)
            .build()
    )
    
2. MediaBrowserService(媒体服务):数据与播放器的持有者
  • 核心职责

    • 运行在后台,持有播放器实例(如 ExoPlayer);
    • 向其他应用提供媒体数据(如歌曲列表、专辑信息);
    • 连接 MediaSession,处理跨进程控制请求。
  • 典型场景
    当车载系统需要获取手机音乐列表时,通过 MediaBrowser 连接到音乐 App 的 MediaBrowserService,服务通过 onLoadChildren 方法返回歌曲数据。

3. MediaBrowser(媒体浏览器):客户端的连接工具
  • 核心职责

    • 作为客户端(如手机 UI、车载系统)的 “连接器”,连接到后台的 MediaBrowserService;
    • 获取媒体数据(歌曲列表),并监听服务状态变化。
  • 代码示例

    kotlin

    // 连接后台服务
    mediaBrowser = MediaBrowserCompat(
        context,
        ComponentName(context, MusicService::class.java),
        connectionCallback,
        null
    )
    mediaBrowser.connect() // 建立连接
    
4. MediaController(媒体控制器):指令发送器
  • 核心职责

    • 作为 “遥控器”,向 MediaSession 发送控制指令(播放 / 暂停 / 切歌);
    • 获取 MediaSession 的状态,更新 UI(如按钮状态、进度条)。

三、MediaSession 工作流程:从初始化到跨设备控制

  1. 初始化阶段

    • 服务端(MediaBrowserService)创建 MediaSession,并绑定播放器;
    • 客户端(MediaBrowser)连接服务,获取 MediaController。
  2. 状态同步

    • 服务端通过 setMetadata 和 setPlaybackState 更新媒体信息和播放状态;
    • 系统自动将状态同步到通知栏、锁屏(需配置 MediaStyle 通知)。
  3. 跨设备控制

    • 外部设备(如蓝牙耳机)发送控制指令(如暂停);
    • 指令通过系统传递给 MediaSession,再由 MediaSession 通知播放器执行。

四、实战:MediaSession 与 ExoPlayer 结合

kotlin

class MusicService : MediaBrowserServiceCompat() {
    private lateinit var exoPlayer: ExoPlayer
    private lateinit var mediaSession: MediaSessionCompat
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 初始化 ExoPlayer
        exoPlayer = ExoPlayer.Builder(this).build()
        // 初始化 MediaSession
        mediaSession = MediaSessionCompat(this, "MusicService")
        mediaSession.setCallback(object : MediaSessionCompat.Callback() {
            override fun onPlay() {
                exoPlayer.play() // 播放
                updatePlaybackState() // 更新状态
            }
            override fun onPause() {
                exoPlayer.pause() // 暂停
                updatePlaybackState()
            }
        })
        mediaSession.isActive = true // 激活会话
        return START_STICKY // 服务被杀死后尝试重启
    }
    
    private fun updatePlaybackState() {
        val state = if (exoPlayer.isPlaying) {
            PlaybackStateCompat.STATE_PLAYING
        } else {
            PlaybackStateCompat.STATE_PAUSED
        }
        mediaSession.setPlaybackState(
            PlaybackStateCompat.Builder()
                .setState(state, exoPlayer.currentPosition, 1.0f)
                .build()
        )
    }
}

五、核心场景与最佳实践

  1. 通知栏集成

    • 使用 MediaStyle 通知,自动显示 MediaSession 的状态:

      kotlin

      val notification = NotificationCompat.Builder(this, channelId)
          .setStyle(androidx.media.app.NotificationCompat.MediaStyle()
              .setMediaSession(mediaSession.sessionToken))
          .build()
      startForeground(1, notification)
      
  2. 音频焦点与 MediaSession 协同

    • 当应用失去音频焦点时(如来电),MediaSession 应暂停播放:

      kotlin

      when (focusChange) {
          AudioManager.AUDIOFOCUS_LOSS -> {
              mediaSession.setPlaybackState(
                  PlaybackStateCompat.Builder()
                      .setState(PlaybackStateCompat.STATE_PAUSED, ...)
                      .build()
              )
              exoPlayer.pause()
          }
      }
      
  3. Android 12+ 适配

    • 新增 MediaController2 和 MediaSession2,支持更灵活的跨设备控制(如多设备音频路由):

      kotlin

      // 创建 MediaSession2
      val mediaSession2 = MediaSession2.Builder(context, "MusicApp")
          .setPlaybackProperties(PlaybackProperties.Builder()
              .setSupportedActions(PlaybackState.ACTION_PLAY | ACTION_SKIP_TO_NEXT)
              .build())
          .build()
      

六、常见问题与解决方案

  1. 问题:通知栏控制按钮无响应

    • 原因:MediaSession 未正确绑定控制器
    • 方案:确保 MediaStyle 通知关联了 MediaSession 的 sessionToken
  2. 问题:跨设备控制延迟

    • 原因:服务连接超时或状态更新不及时
    • 方案:使用 MediaBrowserCompat 的 onServiceConnected 回调确保连接成功后再发送指令
  3. 问题:后台服务被系统杀死

    • 方案:使用 START_STICKY 模式,并通过 setForegroundServiceType 声明媒体播放类型

七、总结

MediaSession 与 Media 框架是 Android 媒体开发的核心基础设施,其核心价值在于:

  • 统一控制:让手机、耳机、车载等设备都能无缝操作播放器;

  • 状态同步:自动更新通知栏、锁屏等系统界面;

  • 后台持久化:支持应用在后台稳定运行并响应控制。

掌握这套框架,不仅能开发音乐、视频类应用,还能为智能家居、车载系统等跨设备场景提供媒体控制能力。