Android 多媒体框架支持播放各种常见媒体类型,因此 可轻松地将音频、视频和图片集成到您的应用中。你可以播放音频或 从存储在应用资源(原始资源)的媒体文件(原始资源)中获取独立文件 或从通过网络连接到达的数据流中,所有这些均使用 MediaPlayer API。
1. MediaPlayer初始化
java
mMediaPlayer = MediaPlayer()
mMediaPlayer?.let {
it.setAudioStreamType(AudioManager.STREAM_MUSIC)
it.setOnCompletionListener(this)
it.setOnErrorListener(this)
}
MediaPlayer初始化只需要直接创建即可,添加播放完成跟错误监听。一般我们初始化还可以首次将上次播放的音乐跟播放进度设置进去,mMediaPlayer?.setDataSource(music.data)是设置播放音乐数据,mMediaPlayer?.seekTo(lastMusicProgress)是设置播放进度,将播放进度设置到上次播放的位置
java
private fun initMediaPlayer(position: Int = 0) = launchMain {
val lastMusicProgress = LocalKV.getIns().getInt(Constant.LAST_PLAY_PROGRESS, 0)
val music: Music = mPlayList[position]
if (music.data != null) {
try {
mMediaPlayer?.reset()
mMediaPlayer?.setDataSource(music.data)
mMediaPlayer?.prepare()
mMediaPlayer?.seekTo(lastMusicProgress)
mLastMusic = music
} catch (e: Exception) {
LogUtil.i(TAG, "initMediaPlayer error=${e.message}")
}
}
}
2. 播放音乐
如果是同一首歌而且只是暂停可以直接调用MediaPlayer?.start()继续播放,如果不是同一首歌需要重新调用mMediaPlayer?.setDataSource(it.data)设置播放音乐,然后再调用start去播放,如果该首音乐有问题可以直接播放下一首。
java
fun play() {
if (mMusicPosition >= mPlayList.size - 1) {
mMusicPosition = mPlayList.size - 1
LogUtil.i(TAG, "play position exceed music list")
}
mCurrentMusic = mPlayList[mMusicPosition]
mCurrentMusic?.let {
if (it.data == null) {
return
}
if (isCurrentMusic(mCurrentMusic)) {
if (mMediaPlayer?.isPlaying == true) {
pause()
} else {
mMediaPlayer?.start()
}
} else {
mMediaPlayer?.reset()
val f = File(it.data)
if (!f.exists()) {
next()
return
}
try {
mMediaPlayer?.setDataSource(it.data)
mMediaPlayer?.prepare()
mMediaPlayer?.start()
mLastMusic = mCurrentMusic
} catch (e: Exception) {
mLastMusic = null
next()
}
}
}
}
3. 播放下一首实现
这里会增加播放模式的实现,一般播放模式是顺序、单曲循环、随机、列表循环,正常按顺序播放只需要将播放列表的位置加1即可,即播放下一首。随机就是将所有音乐数据进行随机生成一个数字当下标,然后播放该首音乐。
java
fun next() {
if (mPlayList.size <= 0) {
Toast.makeText(
mContext,
"no music",
Toast.LENGTH_SHORT
).show()
return
}
when (mPlayMode) {
LIST_ONCE -> {
mMusicPosition++
if (mPlayMode >= mPlayList.size) {
mMusicPosition = mPlayList.size
} else {
play()
}
}
LIST_CYCLE, SINGLE_CYCLE -> {
mMusicPosition++
mMusicPosition = if (mMusicPosition >= mPlayList.size) 0 else mMusicPosition
play()
}
LIST_RAND -> {
mMusicPosition = getRandomMusic()
play()
}
}
}
/**
* 设置随机position
*
* @return
*/
private fun getRandomMusic(): Int {
if (mPlayList.size <= 0) return 0
if (isListRandNext) {
return mMusicPosition + 1
}
val random = Random()
return random.nextInt(mPlayList.size)
}
4.实现上一首播放
播放上一首跟播放下一首类似,只是播放的位置减1即可。
java
fun previous() {
if (mPlayList.size <= 0) {
Toast.makeText(
mContext,
"no music",
Toast.LENGTH_SHORT
).show()
LogUtil.i(TAG, "play music list is null")
return
}
when (mPlayMode) {
LIST_ONCE -> {
mMusicPosition--
if (mMusicPosition < 0) {
mMusicPosition = 0
LogUtil.i(TAG, "previous is already first music")
} else {
play()
}
}
LIST_CYCLE -> {
mMusicPosition--
if (mMusicPosition <= 0) {
mMusicPosition = mPlayList.size - 1
}
play()
}
SINGLE_CYCLE -> {
if (isListRandNext) {
mMusicPosition = mPlayList.size - 1
} else {
mMusicPosition--
}
if (mMusicPosition < 0) {
mMusicPosition = mPlayList.size - 1
}
play()
}
LIST_RAND -> {
mMusicPosition = getRandomMusic()
play()
}
}
}
5. 实现音乐播放暂停
MediaPlayer开始由Started状态变成Paused状态,先判断当前是否在播放,如果正在播放就调用
MediaPlayer.pause()去暂停当前音乐播放。
java
/**
* 暂停播放
*/
fun pause() {
if (mPlayList.size <= 0) {
LogUtil.i(TAG, "play music list is null")
return
}
if (mMediaPlayer?.isPlaying == true) {
mMediaPlayer?.pause()
}
}
6. 停止音乐播放
当调用stop方法时,MediaPlayer无论正处于Started、Paused、Prepared或PlaybackCompleted中的哪种状态,都将进入Stopped状态。一旦处于Stoped状态,playback将不能开始,直到重新调用prepare或prepareAsync函数,且处于Prepared状态时才可以开始。
java
/**
* 停止播放
*/
fun stop() {
if (mPlayList.size <= 0) {
LogUtil.i(TAG, "play music list is null")
return
}
mMediaPlayer?.let {
if (it.isPlaying) {
it.stop()
it.reset()
mLastMusic = null
}
}
}
7.设置播放速度
java
/**
* 设置播放速度
* @param speed
*/
fun setPlaySpeed(speed: Float) {
mMediaPlayer?.let {
if (it.isPlaying) {
try {
it.playbackParams = it.playbackParams.setSpeed(speed)
} catch (e: Exception) {
next()
}
}
}
}
8. 释放资源
当我们不需要播放或者退出的时候可以调用mMediaPlayer?.release()去释放资源
java
/**
* 释放
*/
fun release() {
mMediaPlayer?.release()
mMediaPlayer = null
}
9. 其它常用方法
一般我们比较常用的就是获取当前播放进度、播放时长
java
/**
* 获取当前播放进度
*/
fun getPlayProgress(): Int {
mMediaPlayer?.let {
it.currentPosition
}
return 0
}
/**
* 获取播放时长
*/
fun getPlayDuration(): Int {
mMediaPlayer?.let {
it.duration
}
return 0
}
10.更新播放进度实现
为了更好的体验我们可以通过定时器去定时更新播放进度,正常我们定时一秒去更新一次即可。
java
mPlayProgress = PlayProgress(this@MusicMainActivity)
private inner class PlayProgress(parent: MusicMainActivity?) : Runnable {
private val weakReference: WeakReference<*>
init {
weakReference = WeakReference<Any?>(parent)
}
override fun run() {
val parent = weakReference.get() as MusicMainActivity?
parent?.let {
parent
try {
parent.mService?.let {
parent.mProgress = it.playProgress
if (parent.mProgress < 0) {
parent.mProgress = 0
}
}
parent.binding.musicSeekBar.progress = parent.mProgress
parent.mHandler?.let {
it.removeCallbacks(parent.mPlayProgress!!)
it.postDelayed(parent.mPlayProgress!!, 1000)
}
} catch (e: Exception) {
LogUtils.i(TAG, "PlayProgress e=${e.cause}")
}
}
}
}
11. 播放完成处理跟播放错误处理
播放完成我们可以根据设置的播放模式去执行对应的播放操作,播放错误正常我们是停止播放停止然后去播放下一首
java
override fun onCompletion(mp: MediaPlayer?) {
LogUtil.i(TAG, "onCompletion =$mMusicPosition")
if (mPlayList.size <= 0) {
Toast.makeText(
mContext,
"no music",
Toast.LENGTH_SHORT
).show()
LogUtil.i(TAG, "onCompletion play list is null")
return
}
when (mPlayMode) {
LIST_ONCE -> {
mMusicPosition++
if (mPlayMode >= mPlayList.size) {
mMusicPosition = mPlayList.size - 1
pause()
} else {
play()
}
}
LIST_CYCLE -> {
mMusicPosition++
mMusicPosition = if (mMusicPosition >= mPlayList.size) 0 else mMusicPosition
play()
}
SINGLE_CYCLE -> {
mMusicPosition = if (mMusicPosition >= mPlayList.size) 0 else mMusicPosition
play()
}
LIST_RAND -> {
mMusicPosition = getRandomMusic()
play()
}
}
}
override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
mMediaPlayer?.reset()
stop()
next()
return true
}
到这里我们就可以实现一个简单的音乐播放器了。