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
    }

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

相关推荐
阿巴斯甜13 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker14 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952715 小时前
Andorid Google 登录接入文档
android
黄林晴16 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android