前言
在本篇文章中,我们将学习如何在 Android 中播放音频文件。
音乐会代替言语说话。
在 Android 应用程序中添加音频文件有多种方法。例如,您可以使用 ExoPlayer 或 MediaPlayer 在 Android 应用程序中播放音频文件。
在本文中,您将学习如何在 Android 中使用MediaPlayer播放音频文件。以下是您将在此博客中学习的内容:
- 媒体播放器介绍
- MediaPlayer的状态图
- 准备()与准备异步()
- 使用后释放 MediaPlayer
- 两行 MediaPlayer 示例
- 更高级的例子
- 结束语
让我们开始吧。
媒体播放器介绍
为了在 Android 中播放音频或视频文件,Android 多媒体框架包括对 MediaPlayer API 的支持。因此,通过使用 MediaPlayer API,您可以播放 Android 文件系统中的音频/视频文件或播放应用程序资源文件中的文件,甚至可以像 Spotify 一样流式传输音频/视频文件。
以下是可以使用 MediaPlayer 执行的操作:
- 准备媒体文件:要播放媒体文件,您需要先准备它,即您需要加载文件进行播放。用于执行此操作的方法是prepare()、prepareAsync()和setDataSource()。
- 开始/暂停播放:加载媒体文件后,您可以使用start()方法开始播放媒体文件。同样,您可以使用pause()方法暂停正在播放的媒体文件。
- 停止播放:您可以使用reset()方法停止播放。当我们想要停止播放然后准备另一个播放进行播放时使用此方法。
- 查找位置:您可以使用seekTo(position)方法直接跳到媒体文件中的特定位置(以毫秒为单位)。
- 歌曲长度:您可以使用getDuration()方法找到特定歌曲的长度(以毫秒为单位)。
- 歌曲的当前位置:可以通过getCurrentPosition()方法获取当前播放位置。
- 资源释放:您可以使用release()方法释放媒体播放器使用的内存和资源。
这些是可以使用 MediaPlayer API 执行的一些操作。那么,我们应该开始为 MediaPlayer 编写代码吗?不,一个很大的不。在进入代码之前,让我们看一下 MediaPlayer 的状态图,因为了解状态图非常重要,否则,您的应用程序可能会导致一些不必要的崩溃。
MediaPlayer的状态图
在本节中,我们将了解从开始播放到停止播放的不同状态。这些状态是:
- 空闲状态:当您使用new关键字创建 MediaPlayer 对象时,该对象处于空闲状态。此外,当您调用reset()方法时,对象将进入空闲状态。在空闲状态下,MediaPlayer 对象没有任何反应。它仅表示对象已准备好播放某些媒体文件。因此,在空闲状态下,如果您尝试使用getCurrentPosition()方法获取歌曲的当前位置,或者您尝试使用pause()、start()、stop()、setVolume(), getDuration等方法,那么您将遇到一些不必要的应用程序崩溃。
- 错误状态:如果您在使用reset()方法后在空闲状态中使用上述方法,则将调用OnErrorListener.onError()并将您的对象转移到错误状态。但是如果您使用“ new”关键字,则不会调用 OnErrorListener.onError() 并且状态将保持不变。要重用存在于错误状态中的对象,您需要调用reset()方法。
- 结束状态:当您调用release()方法时,MediaPlayer 对象占用的所有资源都将被释放,对象将进入结束状态。如果对象处于结束状态,则无法将对象回调到其他状态。
- 初始化状态: MediaPlayer 对象的整个生命周期介于空闲和结束状态之间。在空闲状态之后,您需要提供您要使用的数据源,即媒体源。因此,setDataSource()方法用于相同目的,之后,对象将进入初始化状态。当应用程序处于空闲状态时,应始终使用 setDataSource(),否则将通过 IllegalStateException。
- 准备状态:在开始播放音乐之前,需要将 MediaPlayer 对象置于准备状态。在准备状态下,您可以调整视频/音频的循环或设置音量。因此,要将对象从初始化状态带入准备状态,您可以使用prepare()方法直接将对象转移到准备状态,也可以使用一种称为准备状态的中间状态。要从初始化状态进入准备状态,可以调用prepareAsync()方法将对象传输到准备状态,当准备完成时,调用OnPreparedListener接口的onPrepared()方法并将对象转移到准备状态。prepare()和prepareAsync()必须仅在此状态下调用,否则将通过 IllegalStateException。
- 开始状态:在准备状态之后,如果你使用start()方法播放任何音频/视频文件,则对象进入开始状态,在这里你可以应用一些方法,如seekTo()或start()。如果您在start 状态下使用start(),那很好,不会发生任何事情。
- 暂停状态:在播放音频或视频文件时,如果您暂停媒体,则它会进入暂停状态。您可以使用pause()方法将媒体置于暂停状态。为了恢复媒体,您可以使用start()方法。恢复的播放位置与暂停的位置相同,使用start()方法后,播放将恢复,对象将移动到启动状态。在暂停状态下调用pause()方法没有任何效果。
- 停止状态:当您从准备、启动、暂停或播放完成状态使用stop()方法时, MediaPlayer 对象将进入停止状态,为了再次播放音频/视频,您需要将对象发送到通过调用prepare()或prepareAsync()方法准备好的状态。在停止状态下调用stop()方法没有任何效果。
- PlaybackCompleted 状态:如果循环设置为真,即您想一次又一次地播放媒体文件,则对象应保持在启动状态。如果循环设置为false,则OnCompletion.onCompletion()将在媒体文件完成后调用,并且对象将进入PlaybackCompleted状态。为了重新开始播放,您可以调用start()方法。
下图是我们在本博客上述部分讨论的内容的总结。在使用 MediaPlayer 之前了解它的状态非常重要,因为有些方法必须仅在特定状态下调用,否则会出现异常。因此,最好了解所有状态。
准备()与准备异步()
在上面的部分中,我们已经看到要将 MediaPlayer 对象从初始化状态带入准备状态,我们有两个选择,即我们可以使用prepare()方法,对象将直接进入准备状态,或者我们可以使用中间状态,即准备状态将对象发送到准备状态。要从初始化状态进入准备状态,我们可以使用prepareAsync()方法。那么, prepare()和prepareAsync()方法有什么区别呢?
如果您正在调用prepare()方法,那么它将获取并解码您的媒体数据,此过程可能需要一些时间。因此,如果您从应用程序的 UI 线程调用此prepare()方法,那么这可能会导致您的应用程序出现延迟,最终您将失去用户。因此,您可以在其他线程上运行它,而不是在 UI 线程上运行prepare()方法,当媒体数据的获取和解码完成后,该线程应通知主线程,主线程将执行休息。所以,这个过程是在prepareAsync()方法的帮助下完成的。
发布媒体播放器
在上一节中,我们已经看到了 MediaPlayer 的所有状态。在继续之前,我想提一下我们都应该遵循的重要一点,即在使用 MediaPlayer 对象后释放它。
每当我们使用 MediaPlayer 时,MediaPlayer 都会获取各种系统资源。因此,当您使用完 MediaPlayer 后,您应该通过调用release()方法释放 MediaPlayer 获取的所有资源。此外,如果您没有在后台运行媒体应用程序,那么您应该在应用程序处于onPause()状态时释放所有资源。释放资源是重要的一步,因为如果您不执行此操作,那么您的应用程序可能会导致糟糕的用户体验。
两行 MediaPlayer 示例
在这一部分,我将讨论 MediaPlayer 的一个基本示例,即如何仅通过打开一个活动来播放媒体文件。在这里,我不会处理 MediaPlayer 的任何状态,也不会使用与 MediaPlayer 相关的任何高级方法,即 seekTo()、getCurrentPosition()、getDuration() 等。所以,让我们看一下 MediaPlayer 的两行示例。
使用 EmptyActivity 模板在 Android Studio 中创建一个新项目。在这里,在这个例子中,我们将拥有一个音频文件,我们将在我们的应用程序中使用这个文件。因此,在app/res目录中,通过右键单击 res 目录创建原始目录,然后单击新建,然后单击Android Resource Directory。将资源类型选择为raw,然后单击OK。在您创建的原始目录中,放置您的音频文件,我们稍后将使用此音频文件。
如果要流式传输音频文件,则必须在AndroidManifest.xml文件中允许 Internet 权限。
<uses-permission android:name="android.permission.INTERNET" />
现在,要从您的资源目录 ( raw ) 播放音频文件,只需添加以下两行代码,您就可以使用您的两行音乐应用程序了。
var mediaPlayer: MediaPlayer? = MediaPlayer.create(context, R.raw.sample_media)
mediaPlayer?.start() // no need to call prepare(); create() does that for you
运行您的应用程序并欣赏音乐
在从文件系统播放音乐的情况下,您可以使用getDataSource()方法并将您的 URI 作为参数传递给它。
val sampleUri: Uri = .... // your uri here
val mediaPlayer: MediaPlayer? = MediaPlayer().apply {
setAudioStreamType(AudioManager.STREAM_MUSIC)
setDataSource(applicationContext, sampleUri) //to set media source and send the object to the initialized state
prepare() //to send the object to prepared state
start() //to start the music and send the object to the started state
}
此外,要从 URL 播放某些媒体文件,您可以使用上述 URI 过程来完成,并且可以在 URI 的位置放置音频文件的 URL。
val sampleUrl = "http://........" // your URL here
val mediaPlayer: MediaPlayer? = MediaPlayer().apply {
setAudioStreamType(AudioManager.STREAM_MUSIC) //to send the object to the initialized state
setDataSource(sampleUrl) //to set media source and send the object to the initialized state
prepare() //to send the object to the prepared state, this may take time for fetching and decoding
start() //to start the music and send the object to started state
}
在上面的例子中,我们缺少一些推荐的步骤,即我们没有在使用对象后释放它。此外,我们不会处理 MediaPlayer 的所有状态。那么,让我们看一下使用所有功能的 MediaPlayer 的一些高级示例。
更高级的例子
在此示例中,我们将构建一个媒体播放器,在该媒体播放器中,我们可以播放、暂停和停止媒体文件。此外,我们可以将搜索栏移动到某个位置以从该位置播放音乐。在这个例子中,我将只关注逻辑部分。那么,让我们开始吧。
在您的app/res/raw目录中,粘贴您的音频文件。
现在,为播放、暂停和停止制作三个按钮。同时,您可以添加一个搜索栏来显示音频文件的计时器(这是一个可选步骤)。
现在,在MainActivity.kt文件中,您需要制作 MediaPlayer 的对象。
private lateinit var mediaPlayer: MediaPlayer
现在,对于播放按钮,有两种情况:
- 音频处于暂停状态:当音频处于暂停状态时,您必须使用 getC urrentPosition找到音频的当前位置,然后启动媒体播放器。
- 音频处于准备状态:当音频处于准备状态时,您可以使用 start() 方法启动音频。
以下是相同的代码:
// Start the media player
playBtn.setOnClickListener{
if(pause){ //initially, pause is set to false
mediaPlayer.seekTo(mediaPlayer.currentPosition)
mediaPlayer.start()
pause = false
//playing audio when in paused state
}else{
mediaPlayer = MediaPlayer.create(applicationContext,R.raw.demo)
mediaPlayer.start()
//playing audio when in prepared state
}
}
对于暂停按钮,只有一种情况,即如果正在播放音频,那么您只需使用pause()方法暂停音频。以下是相同的代码:
if(mediaPlayer.isPlaying){
mediaPlayer.pause()
pause = true
//audio is paused here
}
对于停止按钮,您必须检查音频是否仍在播放或处于暂停状态,因为在进入停止状态之前,音频可能处于启动或暂停状态。如果是这样,那么stop(),reset(),最后是release()播放器。以下是相同的代码:
if(mediaPlayer.isPlaying || pause.equals(true)){
pause = false
mediaPlayer.stop()
mediaPlayer.reset()
mediaPlayer.release()
//audio is stopped here
}
就是这样,你会适合使用MediaPlayer。
结束语
在这篇文章中,我们了解了 MediaPlayer 以及如何使用它。我们看到了 MediaPlayer 对象的生命周期。我们看到了 prepare() 和 prepareAsync() 之间的区别。最后,我们看到了一些 MediaPlayer 的例子。