MediaSession框架介绍

介绍

很多同学在学习安卓的时候,可能会完成一个音乐播放器的项目,因为音乐播放器项目会贯穿安卓的四大组件。在项目中,大家一定会在服务中使用MediaPlayer去播放音乐,但是界面如何控制服务进行音乐的更换、改变播放进度,大家往往会使用发送广播的方式去通知服务,同时,服务可以发送广播通知界面播放进度的变化。

这时候问题来了:

1、你喜欢在广播接受者中完成解析数据变为指令的代码吗?

2、你觉得用广播高效吗?

当然,你可以通过binder做为服务的代理来进行通讯,但是对于服务和界面的同步则又略显捉急。(如果做过音乐播放器的同学当然知道我在说些什么)

在安卓5中,谷歌推出了MediaSession框架专门解决媒体播放时界面和服务通讯问题。

要理解MediaSession框架,分别看看Media和Session:首先Media是媒体的意思,也就是说这个框架用于音视频媒体;而Session呢,翻译成中文就是会话的意思。一个会话,肯定是涉及两方或以上;在MediaSession框架中,有受控端(一个)和控制端(可以有多个)。接下来为了保证受控端和控制端不串号(想象一个遥控器可以遥控同一型号的多台电视),就有了SessionToken的概念,相当于我们在连接蓝牙设备时的配对码,这样就保证了不串号。在MediaSession框架中,最重要的三个类的概念就这么多,接下来我们一起看看如何使用MediSession框架。

先放一张框架示意图:

上图你看了可能会丈二和尚摸不清头脑,请耐心地看下面的内容,一会回来看,你会觉得更加清晰了。

框架的主要类:

概念

android.media.session.MediaSession

受控端

android.media.session.MediaSession.Token

配对密钥

android.media.session.MediaController

控制端

android.media.session.MediaSession.Callback

受控端回调,可以接受到控制端的指令

android.media.session.MediaController.TransportControls

控制端的遥控器,用于发送指令

android.media.session.MediaController.Callback

控制端回调,可以接受到受控端的状态

一、基本框架搭建

1:在服务中通过new MediaSession( Context, String)构造出MediaSession,其中字符串可以传入包名(或任意)

2:在服务中调用mediaSession.getSessionToken获得Token对象

3:通过IBinder把Token传递给绑定服务的Activity

4:在Activity中绑定服务,拿到Token对象,并调用MediaController(Context, MediaSession.Token)获得MediaController对象

二、设备上的音乐加载

以上就完成了MediaSession框架的搭建。接下来就开始使用这个框架了。接下来我们看看如何使用框架去完成音乐列表加载。

1、   服务中通过MediaStore内容提供者查询设备上的音乐得到Cursor对象

2、   遍历Cursor把查询的结果封装到List<MediaMetadata>集合中

MediaMetadata可以看作是一个map集合,键是String(需要MediaMetadata上的常量),值是音乐名称、歌手、时常等信息

3、   把List<MediaMetadata>转化成List<MediaSession.QueueItem>

QueueItem和MediaMetadata是什么关系呢?QueueItem在构造的时候,需要MediaDescription,而MediaDescription可以通过MediaMetadata获得。在构造QueueItem时,注意id不重复。

4、   服务中的MediaSession调用setQueue(List<MediaSession.QueueItem>)方法,来告知整个框架的各方,目前有哪些音乐可以播放。

5、   界面上可以通过MediaController的getQueue方法获得播放列表(List<MediaSession.QueueItem>)。当然了因为服务对音乐列表的查询封装会需要一些时间,那也可以给调用mediaController.registerCallback(MediaController.Callback) 给mediaController注册一个监听,每当受控端调用了setQueue方法,所有的回调的onQueueChanged(List<MediaSession.QueueItem>)都会被调用

三、音乐的播放

1、在Activity中调用MediaController的getTransportControls()获得TransportControls对象

2、在播放按钮的点击事件上,调用TransportControls的play方法,

3、为了接受到界面上的play指令,需要在服务端的MediaSession上调用setCallback(MediaSession.Callback)方法,并实现MediaSession.Callback的onPlay方法,

4、在onPlay方法中,服务端可以从播放列表中选取一首音乐去播放,

5、这时候界面上并不知道音乐已经播放了,就需要服务去通知界面,开始播放音乐了,服务中需要调用MediaSession的setPlaybackState(PlaybackState) 去通知界面开始播放了,对于PlaybackState对象,需要用它的构造去Builder去构造,你可以简单的只用setState(int state, long position, float playbackSpeed)方法,其中state是PlaybackState的常量、position就是当前播放位置(可以从MediaPlayer上去获取),而playbackSpeed默认是1就好了

6、现在界面要想知道播放状态发生变化了,可实现MediaController.Callback的onPlaybackStateChanged方法,判断如果状态是正在播放,则可以改变播放按钮的状态为暂停样式。

现在你可以再看一遍上面的步骤,然后对照示意图。需要特别说明的是MediaSession框架只负责通讯,并不涉及任何业务逻辑,具体对MediaPlayer的方法调用、音乐加载、最重要的就是明白TransportControls方法和MediaSession.Callback回调的对应关系

意义

TransportControls

MediaSession.Callback

说明

播放

play()

onPlay()

 

停止

stop()

onStop()

 

暂停

pause()

onPause()

 

指定播放位置

seekTo(long pos)

onSeekTo(long)

 

快进

fastForward()

onFastForward()

 

回倒

rewind()

onRewind()

 

下一首

skipToNext()

onSkipToNext()

 

上一首

skipToPrevious()

onSkipToPrevious()

 

指定id播放

skipToQueueItem(long)

onSkipToQueueItem(long)

指定的是Queue的id,即QueueItem构造的id

指定id播放

playFromMediaId(String , Bundle )

onPlayFromMediaId(String , Bundle )

指定的是MediaMetadata的id

搜索播放

playFromSearch(String , Bundle )

onPlayFromSearch(String , Bundle )

需求不常见

指定uri播放

playFromUri(Uri , Bundle )

onPlayFromUri(Uri , Bundle )

需求不常见

发送自定义动作

sendCustomAction(String , Bundle )

onCustomAction(String , Bundle )

可用来更换播放模式、重新加载音乐列表等

打分

setRating(Rating rating)

onSetRating(Rating)

内置的评分系统有星级、红心、赞/踩、百分比

以及MediaSession的方法和MediaController.Callback的回调方法

意义

MediaSession

MediaController.Callback

说明

当前播放音乐

setMetadata(MediaMetadata )

onMetadataChanged(MediaMetadata )

 

播放状态

setPlaybackState(PlaybackState )

onPlaybackStateChanged(PlaybackState )

 

播放队列

setQueue(List<MediaSession.QueueItem> )

onQueueChanged(List<MediaSession.QueueItem> )

 

播放队列标题

setQueueTitle(CharSequence )

onQueueTitleChanged(CharSequence )

不常用

额外信息

setExtras(Bundle )

onExtrasChanged(Bundle )

可以记录播放模式

自定义事件

sendSessionEvent(String , Bundle )

onSessionEvent(String , Bundle )

不常用

四、MediaSession的精妙之处

MediaSession框架中个人感觉最妙的部分就是播放进度的获取了

如果在原来,可通过不断地调用MediaPlayer的getPosition获取播放进度,但如果项目的整体架构比较好的话,界面是拿不到MediaPlayer对象的。在MediaSession框架中,完全不需要去获取播放进度,当然前提是播放状态是准确的。

我们来看看PlaybackState.Builder的setState方法:

setState(int state, long position, float playbackSpeed)

setState(int state, long position, float playbackSpeed, long updateTime)

第二个的方法比第一个的多了一个参数叫更新时间,其实第一个方法会调用第二个方法,并指定更新时间为开机至今的时间(因为开机时间无法更改,系统时间可以改)。

在界面上上如何获得当前播放进度呢:

计算公式如下

((获取当前开机时间 – 上次更新状态的时间)* 播放速度 + 上次更新状态时的播放进度)

代码如下

long currentPosition = ((SystemClock.elapsedRealtime() – playbackState.getLastPositionUpdateTime() ) * playbackState. getPlaybackSpeed() ) + playbackState.getPosition();

总结

MediaSession框架对于播放的各种需求都非常优雅地提供了实现,这些都等待你的发现,而且还提供了扩展的控件,可以自己完成一些自定义的请求。

具体的代码,可以参照谷歌官方的项目:https://github.com/googlesamples/android-UniversalMusicPlayer。正常运行项目需要FQ,因为这个应用的音乐资源是从youtube上加载的。在看代码的时候需要把无关代码快速跳过,把握主线。

现在有同学可能会问了,MediaSession框架只能在安卓5系统上使用,但现在安卓5的市场占有率还比较低,如何兼容低版本呢?Google公司在support-v4(21以上版本)中也提供了MediaSessionCompact(android.support.v4.media.session.MediaSessionCompat)兼容包。具体API大同小异。

转载于:https://www.cnblogs.com/whitcast/p/5949246.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值