Android 媒体播放框架MediaSession分析与实践

本文详细介绍了Android MediaSession框架,旨在解决媒体播放时Activity和Service的通讯问题。通过MediaSession、MediaBrowser、MediaBrowserService和MediaController四大核心成员的讲解,展示了如何构建一个简单的音乐播放器。MediaSession框架简化了音乐播放App的架构,减少了广播的使用,提高了代码的可读性和耦合度。此外,还预告了对Google官方开源项目UniversalMusicPlayer的源码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

版权声明:本文为博主原创文章,未经博主允许不得转载
源码:AnliaLee/BauzMusic
大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论

前言

最近一直在忙着学习和研究音乐播放器,发现介绍MediaSession框架的资料非常少,更多的是一些源码和开源库,这对于初学者来说不是很友好,可能看着看着就绕晕了,遂博主决定动手写点这方面的博客分享给大家

参考资料
googlesamples/android-UniversalMusicPlayer
Media Apps Overview(有前辈翻译后的版本Android媒体应用(一)


MediaSession框架简介

我们先来看看如何设计一款音乐播放App的架构,传统的做法是这样的:

  • 注册一个Service,用于异步获取音乐库数据、音乐控制等,在Service中我们可能还需要自定义一些状态值回调接口用于流程控制
  • 通过广播(其他方式如接口Messenger都可以)实现ActivityService之间的通信,使得用户可以通过界面上的组件控制音乐的播放、暂停、拖动进度条等操作

如果我们的音乐播放器还需要支持通知栏快捷控制音乐播放的功能,那么又得新增一套广播和相应的接口去响应通知栏按钮的事件

如果还需要支持多端(电视、手表、耳机等)控制同一个播放器,那么整个系统架构可能会变得非常复杂,我们要花费大量的时间和精力去设计、优化代码的结构。那么有什么方法可以节省这些工作,提高我们的效率,然后还可以优雅地实现上述这些功能呢?

GoogleAndroid 5.0中加入了MediaSession框架(在support-v4中同样提供了相应的兼容包,相关的类以Compat结尾,Api基本相同),专门用来解决媒体播放时界面和Service通讯的问题,意在规范上述这些功能的流程。使用这个框架我们可以减少一些流程复杂的开发工作,例如使用各种广播来控制播放器,而且其代码可读性、结构耦合度方面都控制得非常好,因此推荐大家尝试下这个框架。下面我们就开始介绍MediaSession框架的核心成员和使用流程


MediaSession框架的使用

常用成员类概述

MediaSession框架中有四个常用的成员类,它们是整个流程控制的核心

  • MediaBrowser
    媒体浏览器,用来连接MediaBrowserService订阅数据,通过它的回调接口我们可以获取和Service的连接状态以及获取在Service中异步获取的音乐库数据。媒体浏览器一般创建于客户端(可以理解为各个终端负责控制音乐播放的界面)中

  • MediaBrowserService
    浏览器服务,提供onGetRoot(控制客户端媒体浏览器的连接请求,通过返回值决定是否允许该客户端连接服务)和onLoadChildren(媒体浏览器向Service发送数据订阅时调用,一般在这执行异步获取数据的操作,最后将数据发送至媒体浏览器的回调接口中)这两个抽象方法
    同时MediaBrowserService还作为承载媒体播放器(如MediaPlayer、ExoPlayer等)和MediaSession的容器

  • MediaSession
    媒体会话,即受控端,通过设置MediaSessionCompat.Callback回调来接收媒体控制器MediaController发送的指令,当收到指令时会触发Callback中各个指令对应的回调方法(回调方法中会执行播放器相应的操作,如播放、暂停等)。Session一般在Service.onCreate方法中创建,最后需调用setSessionToken方法设置用于和控制器配对的令牌并通知浏览器连接服务成功

  • MediaController
    媒体控制器,在客户端中开发者不仅可以使用控制器向Service中的受控端发送指令,还可以通过设置MediaControllerCompat.Callback回调方法接收受控端的状态,从而根据相应的状态刷新界面UIMediaController的创建需要受控端的配对令牌,因此需在浏览器成功连接服务的回调执行创建的操作

通过上述的简介中我们不难看出这四个成员之间有着非常明确的分工和作用范围,使得整个代码结构变得清晰易读。可以通过下面这张图来简单归纳它们之间的关系

除此之外,MediaSession框架中还有一些同样重要的类需要拿出来讲,例如封装了各种播放状态PlaybackState,和Map相似通过键值对保存媒体信息MediaMetadata,以及用于MediaBrowserMediaBrowserService之间进行数据交互的MediaItem等等,下面我们通过实现一个简单的demo来具体分析这套框架的工作流程

使用MediaSession框架构建简单的音乐播放器

例如我们的demo是这样的(见下图),只提供简单的播放暂停操作,音乐数据源从raw资源文件夹中获取

按照工作流程,我们就从获取音乐库数据开始吧。首先界面上方添加一个RecyclerView来展示获取的音乐列表,我们在DemoActivity中完成一些RecyclerView的初始化操作

public class DemoActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private List<MediaBrowserCompat.MediaItem> list;
    private DemoAdapter demoAdapter;
    private LinearLayoutManager layoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);

        list = new ArrayList<>();
        layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        demoAdapter = new DemoAdapter(this,list);

        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(demoAdapter);
    }
}
复制代码

注意List元素的类型为MediaBrowserCompat.MediaItem,因为MediaBrowser从服务中获取的每一首音乐都会封装成MediaItem对象。接下来我们创建MediaBrowser,并执行连接服务端和订阅数据的操作

public class DemoActivity extends AppCompatActivity {
    ...
    private MediaBrowserCompat mBrowser;

    @Override
    protected void 
### 使用 MediaSession 框架 ViewModel 的 Android Java 集成 在 Android 开发中,`MediaSession` 是用于管理媒体播放的核心组件之一。它允许应用程序控制音频焦点、处理远程控件以及其他应用共享媒体状态。而 `ViewModel` 则是一种架构组件,旨在存储和管理界面相关的数据,以便在配置更改(如屏幕旋转)时保持数据一致。 以下是关于如何将 `MediaSession` 和 `ViewModel` 结合使用的具体实现方法: #### 1. 创建 MediaSession 实例 通过创建一个 `MediaSessionCompat` 对象来初始化会话实例,并将其设置到 `MediaPlayer` 或其他媒体控制器上[^2]。 ```java import android.media.session.MediaSessionCompat; import androidx.lifecycle.ViewModel; public class MediaPlayerViewModel extends ViewModel { private final MediaSessionCompat mediaSession; public MediaPlayerViewModel() { this.mediaSession = new MediaSessionCompat(/* context */, /* tag */); // 设置回调函数以响应外部事件 mediaSession.setCallback(new MediaSessionCompat.Callback() { @Override public void onPlay() { super.onPlay(); // 处理播放逻辑 } @Override public void onPause() { super.onPause(); // 处理暂停逻辑 } @Override public boolean onMediaButtonEvent(Intent mediaButtonIntent) { // 响应媒体按钮点击事件 return super.onMediaButtonEvent(mediaButtonIntent); } }); // 启动会话并使其可发现 mediaSession.setActive(true); } public MediaSessionCompat getMediaSession() { return mediaSession; } } ``` #### 2. 将 MediaSession 绑定至 Activity 或 Fragment 为了使视图层能够访问 `MediaSession` 数据,在绑定过程中可以利用 `LiveData` 来观察变化[^3]。 ```java import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; public class MainActivity extends AppCompatActivity { private MediaPlayerViewModel mediaPlayerViewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化 ViewModel mediaPlayerViewModel = new ViewModelProvider(this).get(MediaPlayerViewModel.class); // 订阅 LiveData 更新 mediaPlayerViewModel.getPlaybackState().observe(this, new Observer<Integer>() { @Override public void onChanged(Integer playbackState) { switch (playbackState) { case PlaybackStateCompat.STATE_PLAYING: updateUIForPlaying(); break; case PlaybackStateCompat.STATE_PAUSED: updateUIForPaused(); break; default: resetUI(); break; } } }); // 获取 MediaSession 并关联操作 MediaSessionCompat session = mediaPlayerViewModel.getMediaSession(); setMediaController(session); } private void setMediaController(MediaSessionCompat session) { MediaControllerCompat controller = new MediaControllerCompat(this, session); MediaControllerCompat.setMediaController(this, controller); } } ``` #### 3. 控制器交互设计 当用户触发某些动作时(例如按下播放/暂停键),可以通过调用 `MediaControllerCompat.TransportControls` 方法发送命令给后台服务或当前活动中的播放器[^4]。 ```java // 调用 Transport Controls 执行特定功能 mediaController.getTransportControls().play(); mediaController.getTransportControls().pause(); mediaController.getTransportControls().skipToNext(); mediaController.getTransportControls().rewind(); ``` 以上代码展示了如何在一个典型的 Android 应用程序中集成 `MediaSession` 和 `ViewModel` 构件。这种模式不仅提高了代码模块化程度,还增强了用户体验的一致性和可靠性。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值