Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Android SDK
Code

使用MediaSessionCompat开发安卓中的背景音乐

by
Difficulty:BeginnerLength:LongLanguages:

Chinese (Traditional) (中文(繁體)) translation by Zhang Xiang Liang (you can also view the original English article)

移动设备最受欢迎的用途之一是通过音乐流媒体服务播放音乐,下载音乐或其他音频源。 虽然这个功能很常见,但是很难实现,因为需要正确构建许多不同的部分以便为用户提供完整的Android体验。 

在本教程中,您将了解如何使用Android支持库中的MediaSessionCompat为用户创建合适的后台音频服务。

设置

需要做的第一件事是将Android支持库引入项目。 将以下行添加到module级的build.gradle文件中。

同步项目之后,创建一个新的Java类。 对于这个例子我将调用类BackgroundAudioService。 我们还将实现以下接口:MediaPlayer.OnCompletionListenerAudioManager.OnAudioFocusChangeListener

现在MediaBrowserServiceCompat实现已创建,让我们花点时间更新AndroidManifest.xml,然后返回此类。 在xml文件中,您需要申请WAKE_LOCK权限。

接下来,在application节点内,使用以下intent-filter项目声明新服务。 这些将允许服务拦截设备的控制按钮,耳机事件和媒体浏览,例如Android Auto(尽管我们不会在本教程中对Android Auto做任何事情,但仍然需要MediaBrowserServiceCompat的基本支持)。

最后,需要声明使用MediaButtonReceiver Android支持库。 这将允许您拦截运行在KitKat系统和更早版本设备上的媒体控制按钮交互和耳机事件。

现在AndroidManifest.xml文件已经完成,可以关闭它了。 我们还将创建一个名为MediaStyleHelper的类,这是由Google开发者倡导者Ian Lake撰写用来清理媒体样式通知的。

一旦创建,请关闭该文件。 下一节将介绍背景音频服务。

构建背景音频服务

现在是时候介绍创建媒体应用程序的核心了。 有几个成员变量需要首先声明:MediaPlayer 用于实际播放以及MediaSessionCompat用于管理元数据和播放控件状态的对象。

此外,还需要BroadcastReceiver来监听耳机状态的更改。 为了简单起见,该接收者将暂停MediaPlayer播放。

对于final类型的成员变量,需要创建  MediaSessionCompat.Callback对象,用于在媒体会话变化时处理播放状态。

我们将在本教程的后面重新阅读上述方法,因为它们将用于驱动我们的媒体应用程序。

我们还需要声明两个方法,虽然它们不需要为本教程做任何事情:onGetRoot()和onLoadChildren()。 可以默认使用以下代码。

最后,需要重写onStartCommand()方法,这是Service的入口。 该方法将使用传递给Service并将其发送到MediaButtonReceiver类的Intent 。

初始化

现在,基本成员变量已经创建了,是时候初始化所有内容了。 我们将调用onCreate()里面的各种辅助方法来完成初始化。

第一个方法initMediaPlayer()将初始化我们在类顶部创建的MediaPlayer对象,请求部分唤醒锁(这就是为什么我们需要在AndroidManifest.xml中需要相关权限),并设置播放器的音量。

第二个方法initMediaSession()是初始化MediaSessionCompat对象并将其连接到允许我们处理播放和用户输入的媒体按钮和控制方法。 该方法首先创建一个指向Android支持库的MediaButtonReceiver类的ComponentName对象,并使用它来创建一个新的MediaSessionCompat。 然后我们将之前创建的MediaSession.Callback对象传递给它,并设置接收媒体按钮输入和控制信号所需的标志。 接下来,我们创建一个新的Intent用于处理在Lollipop设备之前的媒体按钮输入,并为我们的服务设置媒体会话令牌。

最后,注册我们在类顶部创建的BroadcastReceiver以便监听耳机更改事件。

处理音频焦点

现在,已经初始化了BroadcastReceiverMediaSessionCompatMediaPlayer对象,是时候处理音频的焦点了。 

虽然我们可能会认为自己的音频应用程序是当前最重要的,但是设备上的其他音频应用程序会竞争,例如电子邮件通知或手机游戏。 为了处理这些情况,Android系统使用音频焦点来确定如何处理音频。 

我们要处理的第一种情况是开始播放并尝获取设备焦点。 在MediaSessionCompat.Callback对象中,进入onPlay()方法并添加以下条件检查。

上面的代码将调用一个辅助程序尝试获取焦点,如果失败它将return。 在真正的应用程序中,会更加优雅地处理失败的音频播放。  successfullyRetrievedAudioFocus()方法将获得对系统AudioManager的引用,并尝试请求音频焦点用于流式音乐。 然后它将返回一个boolean值来表示是否请求成功。

你会注意到,我们也在传递this给与服务OnAudioFocusChangeListener关联的requestAudioFocus()方法  。 你需要监听几种不同的状态以便遵循设备应用程序生态系统。

  • AudioManager.AUDIOFOCUS_LOSS:当另一个应用程序请求音频焦点时发生这种情况 。 发生这种情况时,您应该停止在应用程序中播放音频。
  • AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:当另一个应用程序想要播放音频时进入此状态,但它只需要短时间内需要对焦。 您可以使用此状态来暂停音频播放。
  • AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:当请求音频焦点时,会引发“可以”的状态,这意味着可以继续播放,但应将音量降低一点。 当设备播放通知声音时,可能会发生这种情况。
  • AudioManager.AUDIOFOCUS_GAIN我们将讨论的最终状态是AUDIOFOCUS_GAIN。 当可以播放的音频播放完成后,应用程序可以恢复到以前的级别。

onAudioFocusChange()回调可能如下所示:

理解MediaSessionCompat.Callback

现在有了Service的基本结构,是时候研究MediaSessionCompat.Callback了。 在最后一节,给onPlay()添加代码以检查音频焦点是否被授予。 在条件语句下面,需要将MediaSessionCompat对象设置为活动状态STATE_PLAYING,指定合适的action在Lollipop版本以前创建暂停按钮。

setMediaPlaybackState()方法用来辅助创建PlaybackStateCompat.Builder对象,并给它指定适当的动作和状态,然后构建PlaybackStateCompat并将其与MediaSessionCompat对象关联。

重要的是要注意,需要在操作中同时使用ACTION_PLAY_PAUSEACTION_PAUSEACTION_PLAY标记以便在Android Wear上获得正确的控制。

Media notification on Android Wear

回到onPlay()中,您将希望通过使用我们之前定义的MediaStyleHelper类来显示与MediaSessionCompat对象关联的播放通知,然后显示该通知。

最后,在onPlay()方法启动MediaPlayer

Media control notification on an Android Nougat device

当回调接收到暂停命令时,调用onPause()方法。 在这里,您将暂停MediaPlayer,将状态设置为STATE_PAUSED,并显示已暂停的通知。

showPausedNotification()辅助方法看起来类似于showPlayNotification()方法。

我们将讨论回调中的下一个方法  onPlayFromMediaId(),以 StringBundle为参数。 这是用来更改应用程序中音轨/内容的回调方法。 

对于本教程,我们将简单地接受原始资源ID并尝试播放,然后重新初始化会话的元数据。 当被允许将Bundle传入此方法时,可以使用它来自定义媒体播放的其他方面,例如为曲目设置自定义背景音。

现在,我们已经讨论了该回调中使用的两个主要方法,重要的是要知道还有其他可选的方法用来自定义服务。 包括onSeekTo()方法,它允许更改内容的播放位置,onCommand()方法接受String表示命令的类型,Bundle表示有关该命令的额外信息,最后是ResultReceiver回调方法,允许发送自定义命令到Service

撕毁

当我们的音频文件完成后,我们将要决定我们的下一个动作是什么。 虽然你可能想在你的应用程序中播放下一个音频,但是为了简单起见就释放MediaPlayer

最后,我们要在ServiceonDestroy()方法中做一些事情。 首先,获取对系统服务AudioManager的引用,并调用abandonAudioFocus()方法,传参为AudioFocusChangeListener,这将通知设备上的其他应用程序您将放弃音频焦点。 接下来,反注册监听耳机更改的BroadcastReceiver,并释放MediaSessionCompat对象。 最后,取消播放控制通知。

现在,应该有一个正在工作的基本背景音频ServiceMediaSessionCompat用于跨设备播放控制。 虽然创建服务就已经涉及了很多知识,您应该能够从应用程序控制播放、通知、Lollipop设备前锁定屏幕(Lollipop设备及以后使用锁定屏幕上的通知)以及从外部设备(如Android Wear)控制这些操作,一旦Service启动后。

Media lock screen controls on Android Kit Kat

从Activity开始和控制内容

虽然大多数控件都是自动的,但仍然需要从应用控件中启动和控制媒体会话。 最起码,在应用中创建MediaBrowserCompat.ConnectionCallbackMediaControllerCompat.Callback、  MediaBrowserCompatMediaControllerCompat对象。

MediaControllerCompat.Callback有个onPlaybackStateChanged()方法 接收播放状态的变化,可用于保持同步UI。

MediaBrowserCompat.ConnectionCallback有个onConnected()方法在MediaBrowserCompat对象被创建和连接时被调用。 你可以用它来初始化MediaControllerCompat对象,将其链接到MediaControllerCompat.Callback,并将其与ServiceMediaSessionCompat关联。 完成后,从这个方法开始音频播放。

您会注意到上述代码段使用getSupportMediaController().getTransportControls()方法与媒体会话进行通信。 使用相同的技术,您可以在音频服务的  MediaSessionCompat.Callback对象调用onPlay()和onPause()。

完成音频播放后,可以暂停音频服务并断开MediaBrowserCompat对象,Activity被摧毁时执行该操作。

总结

哇! 正如你所见,本文研究了如何创建和使用背景音频服务。 

在本教程中,创建了一个播放简单音频文件的服务,监听音频焦点更改以及指向MediaSessionCompat的链接在Android设备(包括手机和Android Wear)上提供通用播放控制。 如果在本教程中遇到问题,我强烈建议查看Envato Tuts + GitHub上相关的Android项目代码

并在Envato Tuts +上查看其他Android 课程教程


关注我们的公众号
Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.