android 使用MediaPlayer实现音乐播放

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
    }

到这里我们就可以实现一个简单的音乐播放器了。

相关推荐
子非衣3 小时前
MySQL修改JSON格式数据示例
android·mysql·json
openinstall全渠道统计6 小时前
免填邀请码工具:赋能六大核心场景,重构App增长新模型
android·ios·harmonyos
双鱼大猫7 小时前
一句话说透Android里面的ServiceManager的注册服务
android
双鱼大猫7 小时前
一句话说透Android里面的查找服务
android
双鱼大猫7 小时前
一句话说透Android里面的SystemServer进程的作用
android
双鱼大猫7 小时前
一句话说透Android里面的View的绘制流程和实现原理
android
双鱼大猫8 小时前
一句话说透Android里面的Window的内部机制
android
双鱼大猫8 小时前
一句话说透Android里面的为什么要设计Window?
android
双鱼大猫8 小时前
一句话说透Android里面的主线程创建时机,frameworks层面分析
android
苏金标9 小时前
android 快速定位当前页面
android