Android 多媒体框架支持播放各种常见媒体类型,因此 可轻松地将音频、视频和图片集成到您的应用中。你可以播放音频或 从存储在应用资源(原始资源)的媒体文件(原始资源)中获取独立文件 或从通过网络连接到达的数据流中,所有这些均使用 MediaPlayer
API。
1. MediaPlayer初始化
mMediaPlayer = MediaPlayer()mMediaPlayer?.let {it.setAudioStreamType(AudioManager.STREAM_MUSIC)it.setOnCompletionListener(this)it.setOnErrorListener(this)}
MediaPlayer初始化只需要直接创建即可,添加播放完成跟错误监听。一般我们初始化还可以首次将上次播放的音乐跟播放进度设置进去,mMediaPlayer?.setDataSource(music.data)是设置播放音乐数据,mMediaPlayer?.seekTo(lastMusicProgress)是设置播放进度,将播放进度设置到上次播放的位置
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去播放,如果该首音乐有问题可以直接播放下一首。
fun play() {if (mMusicPosition >= mPlayList.size - 1) {mMusicPosition = mPlayList.size - 1LogUtil.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 = nullnext()}}}}
3. 播放下一首实现
这里会增加播放模式的实现,一般播放模式是顺序、单曲循环、随机、列表循环,正常按顺序播放只需要将播放列表的位置加1即可,即播放下一首。随机就是将所有音乐数据进行随机生成一个数字当下标,然后播放该首音乐。
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 mMusicPositionplay()}LIST_RAND -> {mMusicPosition = getRandomMusic()play()}}}/*** 设置随机position** @return*/private fun getRandomMusic(): Int {if (mPlayList.size <= 0) return 0if (isListRandNext) {return mMusicPosition + 1}val random = Random()return random.nextInt(mPlayList.size)}
4.实现上一首播放
播放上一首跟播放下一首类似,只是播放的位置减1即可。
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 = 0LogUtil.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()去暂停当前音乐播放。
/*** 暂停播放*/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状态时才可以开始。
/*** 停止播放*/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.设置播放速度
/*** 设置播放速度* @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()去释放资源
/*** 释放*/fun release() {mMediaPlayer?.release()mMediaPlayer = null}
9. 其它常用方法
一般我们比较常用的就是获取当前播放进度、播放时长
/*** 获取当前播放进度*/fun getPlayProgress(): Int {mMediaPlayer?.let {it.currentPosition}return 0}/*** 获取播放时长*/fun getPlayDuration(): Int {mMediaPlayer?.let {it.duration}return 0}
10.更新播放进度实现
为了更好的体验我们可以通过定时器去定时更新播放进度,正常我们定时一秒去更新一次即可。
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 {parenttry {parent.mService?.let {parent.mProgress = it.playProgressif (parent.mProgress < 0) {parent.mProgress = 0}}parent.binding.musicSeekBar.progress = parent.mProgressparent.mHandler?.let {it.removeCallbacks(parent.mPlayProgress!!)it.postDelayed(parent.mPlayProgress!!, 1000)}} catch (e: Exception) {LogUtils.i(TAG, "PlayProgress e=${e.cause}")}}}}
11. 播放完成处理跟播放错误处理
播放完成我们可以根据设置的播放模式去执行对应的播放操作,播放错误正常我们是停止播放停止然后去播放下一首
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 - 1pause()} else {play()}}LIST_CYCLE -> {mMusicPosition++mMusicPosition = if (mMusicPosition >= mPlayList.size) 0 else mMusicPositionplay()}SINGLE_CYCLE -> {mMusicPosition = if (mMusicPosition >= mPlayList.size) 0 else mMusicPositionplay()}LIST_RAND -> {mMusicPosition = getRandomMusic()play()}}}override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {mMediaPlayer?.reset()stop()next()return true}
到这里我们就可以实现一个简单的音乐播放器了。