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
    }

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

相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui