版权声明:本文为博主原创文章,未经博主允许不得转载
源码:AnliaLee/BauzMusic
大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论
前言
最近一直在忙着学习和研究音乐播放器,发现介绍MediaSession框架的资料非常少,更多的是一些源码和开源库,这对于初学者来说不是很友好,可能看着看着就绕晕了,遂博主决定动手写点这方面的博客分享给大家
参考资料
googlesamples/android-UniversalMusicPlayer
Media Apps Overview(有前辈翻译后的版本Android媒体应用(一))
MediaSession框架简介
我们先来看看如何设计一款音乐播放App的架构,传统的做法是这样的:
- 注册一个Service,用于异步获取音乐库数据、音乐控制等,在Service中我们可能还需要自定义一些状态值和回调接口用于流程控制
- 通过广播(其他方式如接口、Messenger都可以)实现Activity和Service之间的通信,使得用户可以通过界面上的组件控制音乐的播放、暂停、拖动进度条等操作
如果我们的音乐播放器还需要支持通知栏快捷控制音乐播放的功能,那么又得新增一套广播和相应的接口去响应通知栏按钮的事件
如果还需要支持多端(电视、手表、耳机等)控制同一个播放器,那么整个系统架构可能会变得非常复杂,我们要花费大量的时间和精力去设计、优化代码的结构。那么有什么方法可以节省这些工作,提高我们的效率,然后还可以优雅地实现上述这些功能呢?
Google在Android 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回调方法接收受控端的状态,从而根据相应的状态刷新界面UI。MediaController的创建需要受控端的配对令牌,因此需在浏览器成功连接服务的回调执行创建的操作
通过上述的简介中我们不难看出这四个成员之间有着非常明确的分工和作用范围,使得整个代码结构变得清晰易读。可以通过下面这张图来简单归纳它们之间的关系
除此之外,MediaSession框架中还有一些同样重要的类需要拿出来讲,例如封装了各种播放状态的PlaybackState,和Map相似通过键值对保存媒体信息的MediaMetadata,以及用于MediaBrowser和MediaBrowserService之间进行数据交互的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