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
    }

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

相关推荐
长亭外的少年2 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿4 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神5 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛5 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法6 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter7 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快8 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl8 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
麦田里的守望者江9 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin
Dnelic-9 小时前
解决 Android 单元测试 No tests found for given includes:
android·junit·单元测试·问题记录·自学笔记