网易云音乐凭着良好的交互体验,优质丰富的资源在终端一直有着不错的市场。相比较市面上的主流音乐播放器(QQ音乐、虾米音乐),笔者更倾向于云音乐的UED。
单从播放器页来说,云音乐的界面非常简洁,只保留了主要的操作功能,避免过多的信息造成视觉上的疲倦。主视图上不断旋转的黑胶唱片非常有带入感,左滑右滑切换唱片的设计也非常经典。
本文主要介绍了播放器实现的一些核心代码和思路,完整代码请通过文末链接自行clone
必要知识储备:
- 基础数据结构知识(队列、栈、链表)
- AVFoundation/AVPlayer
- AVAudioSession
- CoreAnimation
- Autolayout(Masonry)
- KVO、Notification
实现需求:
- 播放器播放、暂停,上一首、下一首
- 播放模式切换,支持单曲循环、顺序播放、随机播放
- 黑胶唱片仿真动画
- 后台播放及中断控制
- 支持iOS10.0包括及以上系统
播放资源控制
音乐播放器的核心功能是针对播放资源的控制,其核心内容包括媒体资源状态控制、播放器状态控制。
值得庆幸的是我们并不用自己去实现复杂的底层功能,如在线资源拉取、资源缓存(如果有必要实现)、资源解码播放等,此处我们可以选用源生AVFoundation框架下的AVPlayer工具帮助我们完成这些功能,我们要做的是针对AVPlayer提供的接口根据业务进行封装。
本文不介绍AVPlayer的详细使用,只针对具体使用业务场景提供封装思路及核心代码
AVPlayerItem封装
AVPlayerItem
是AVPlayer
播放资源的基本单位,得益于OC语言良好的命名习惯,我们非常容易理解AVPlayerItem类的定义,它管理了媒体资源的地址
、时长
、缓存
,并提供相关状态属性用于监听。下面代码块展示的是被封装的属性:
1 | // AVPlayerItem.h |
值得关注的是,该类的多种状态响应使用了KVO、通知等方式发出,在接口阅读和使用上给我们带来了一定的困难,所以要将状态的监控再封装成我们熟悉的形式(delegate、block),并增加媒体播放的自定义参数:
1 | // CMPlayerItem.h |
这里我们利用了继承的特性,创建了一个符合业务需求的类,并使用了工厂方法提供了一个新的初始化器,用于初始化必要资源并添加监听。对比父类的接口,经过封装以后的API阅读起来是不是容易理解多了~
数据结构分析
在封装AVPlayer
之前,先来分析一下播放器需求。
- 播放器有三种播放模式,顺序、乱序、单曲循环。点击下一曲按钮要根据不同的播放模式获取曲目
- 点击上一曲按钮能够播放最近已经播放过上一首的曲目
- 滑动黑胶唱片能够切换上一曲或下一曲
通过对需求的分析,我们将业务转换为数据结构,播放器的播放资源组织可以通过一个特殊的队列结构进行管理,队列的长度为3,index=1指向播放中的数据,因为存在一个滑动唱片的操作设计,我们必须保证播放队列的内容长度永远为3,否则在滑动时再加载必然会造成页面卡顿。当切换下一曲时丢弃index=0的数据,从资源列表获取下一曲内容。但是这样的数据结构存在一个缺陷,当切换上一曲时,我们永远只能拿到上一次播放的资源。
聪明的同学会发现上一曲功能具有LIFO特性,很像一个栈建构。那么我们将之前的播放队列做一点改造,使用一个栈结构将下一曲切换时丢弃的曲目保存好,当需要上一曲切换时使用栈中的内容补充队列空缺。
有了思路,我们将上一曲切换的流程转化为数据结构图并实现核心代码。首先播放队列丢弃index=2的曲目,并从播放栈中获取栈顶曲目插入播放队列index=0。当播放栈内容为空时,根据不同的播放模式从播放列表直接获取资源插入播放队列
核心代码如下:
1 | - (void)prev { |
下一曲切换操作和上一曲类似,只不过我们要改变一下数据流方向,将播放队列index=0内容丢至播放栈中,并从播放列表中根据当前模式插入播放队列index=2位置
核心代码如下:
1 |
|
AVPlayer封装
和封装AVPlayerItem
思路一样,通过继承的方式我们创建一个新类CMPlayer
,整合父类接口并用我们熟悉的方式暴露新的API,下面是.h文件代码
1 | // CMPlayer.h |
.m文件具体实现代码请通过文末github链接自行clone,此处不再赘述
后台播放控制
允许后台播放
播放器需要在应用切换到后台时,保持音频播放能力。允许后台播放最简单的方式,是在Target - Capability - Background Modes
中进行配置
参考文献:《Enabling Background Audio》
设置AVAudioSession
可以使用AVAudioSession
告诉操作系统如何处理你的App音频流,而不需要和音频硬件产生直接的交互或者操作
设置AVAudioSession
的Category
确定基本事件行为,再使用Mode
Options
两个属性对行为进行微调,下面是播放器设置源码:
1 |
|
参考文献:《Audio Session Programming Guide》
远程控制媒体播放
我们还需支持在锁屏的媒体资源信息展示以及控制中心中对音乐的控制。我们可以使用Media Player框架的MPRemoteCommandCenter
和MPNowPlayingInfoCenter
实现。
MPRemoteCommandCenter
使用事件绑定的方式实现对远程事件的监听处理,每个控制事件都封装了一个MPRemoteCommand
对象,为具体的事件添加响应方法:
1 | // CMPlayer.m |
MPNowPlayingInfoCenter
是一个可以设置当前播放媒体需要展示信息的对象,这些信息会被展示到锁屏界面、控制中心等处,以下是简单用法:
1 | // CMPlayer.m |
参考文献:《Controlling Background Audio》
关于
本文为博主原创文章,项目资源均只供学习请勿商用,转载请附上博文链接!