添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在这里插入图片描述
音频播放,是比较常见或常用的功能,比如 音乐播放器 新闻播报 听书 等等,而恰巧如果你想 自定义 一个音频播放器的话,本文一定对你有帮助!

  • start() 开始播放
  • pause() 暂停播放
  • stop() 停止播放
  • prepare() 资源准备
  • prepareAsync() 异步准备,不阻塞UI线程
  • seekTo(int msec) 定位到指定位置,单位毫秒
  • isLooping 是否循环播放
  • isPlaying 播放状态
  • duration 总时长
  • currentPosition 当前位置
  • release() 资源释放

Component Tree

具体的 xml 代码就不贴了,看一下组件树
在这里插入图片描述

* 初始化 及 资源准备 private fun audioPrepare ( path : String ) { mMediaPlayer = MediaPlayer ( ) . apply { setDataSource ( path ) //支持文件、网络地址、uri prepareAsync ( ) //异步准备,不阻塞UI线程 isLooping = false //循环播放 initMediaPlayerListener ( )

setDataSource ,设置数据源,支持本地文件、网络请求的地址、uri等,看一下源码:

  • setDataSource(FileDescriptor)
  • setDataSource(String)
  • setDataSource(Context, Uri)
  • setDataSource(FileDescriptor, long, long)
  • setDataSource(MediaDataSource)

如果是本地文件,注意 读写 权限。

prepareAsync() 异步准备,不阻塞UI线程

然后看一下调用的 initMediaPlayerListener 方法

播放器监听事件及交互

* 播放器监听事件 private fun initMediaPlayerListener ( ) { mMediaPlayer ? . setOnBufferingUpdateListener { mp , percent -> LogUtil . i ( "缓冲进度 $percent %" ) mMediaPlayer ? . setOnPreparedListener { LogUtil . i ( "准备完成" ) //在准备完成之后获取信息,否则会有异常 val duration = mMediaPlayer ? . duration //时长 val currentPosition = mMediaPlayer ? . currentPosition //当前位置 LogUtil . i ( "当前位置 $currentPosition /时长 $duration " ) tv_currentPosition . text = formatDuration ( currentPosition !! ) tv_duration . text = formatDuration ( duration !! ) seek_bar . max = duration mMediaPlayer ? . setOnCompletionListener { LogUtil . i ( "播放完毕" ) mMediaPlayer ? . setOnErrorListener { mp , what , extra -> LogUtil . i ( "播放错误" ) return @setOnErrorListener true mMediaPlayer ? . setOnSeekCompleteListener { LogUtil . i ( "定位完成" ) seek_bar . setOnSeekBarChangeListener ( object : SeekBar . OnSeekBarChangeListener { override fun onProgressChanged ( seekBar : SeekBar ? , progress : Int , fromUser : Boolean ) { tv_currentPosition . text = formatDuration ( seekBar !! . progress ) override fun onStartTrackingTouch ( seekBar : SeekBar ? ) { override fun onStopTrackingTouch ( seekBar : SeekBar ? ) { //拖动结束之后再设置,如果在onProgressChanged中设置会有杂音 mMediaPlayer ? . seekTo ( seekBar !! . progress ) tv_currentPosition . text = formatDuration ( seekBar !! . progress ) btn_start . setOnClickListener { audioStart ( ) btn_pause . setOnClickListener { audioPause ( ) btn_seek . setOnClickListener { seek_bar . progress = ( seek_bar . max * 0.8 ) . roundToInt ( ) mMediaPlayer ? . seekTo ( seek_bar !! . progress ) tv_currentPosition . text = formatDuration ( seek_bar !! . progress ) audioStart ( ) btn_restart . setOnClickListener { audioRestart ( )

主要 是一些播放器的监听事件和按钮操作事件。

https://blog.csdn.net/yechaoa

着重介绍两个:

1、setOnPreparedListener

注意,在获取资源 时长 的时候,需要在播放器 准备完成 之后获取,否则会有 异常

Attempt to call getDuration in wrong state: mPlayer=0x7244676280, mCurrentState=4
error (-38, 0)

并会回调OnErrorListener

然后设置显示,并把时长赋值给seek_bar的最大值。

2、setOnSeekBarChangeListener

3个方法:

  • onProgressChanged 进度改变
  • onStartTrackingTouch 开始拖动
  • onStopTrackingTouch 停止拖动

我们需要在改变中改变后对当前播放时长进行更新,并在最后的位置进行播放操作。

如果程序上没有定位到指定播放位置这种操作的话,不要在onProgressChanged中执行播放操作,因为频繁的进度改变,频繁的调用播放,会有杂音

所以建议用户手动拖动来触发播放。

如果非要程序可以跳到指定位置播放的话,建议如下操作:

        btn_seek.setOnClickListener {
            seek_bar.progress = (seek_bar.max * 0.8).roundToInt()
            mMediaPlayer?.seekTo(seek_bar!!.progress)
            tv_currentPosition.text = formatDuration(seek_bar!!.progress)
            audioStart()

手动赋值progress ,并调用播放。

格式化播放时间

这个获取时长返回的是毫秒,所以我们还需要对其格式化操作。

* 格式化播放时间 private fun formatDuration(duration: Int): String { val d = duration / 1000 val minute = d / 60 val second = d % 60 val m: String = if (minute < 10) "0$minute" else "$minute" val s: String = if (second < 10) "0$second" else "$second" return "$m:$s"

做了一个判断,不足两位数则前位补0。

* 开始播放 private fun audioStart() { mMediaPlayer?.run { if (!this.isPlaying) { start() startTimer()

因为没有播放中的回调接口,所以这里启动一个Timer获取当前位置并更新UI

Timer更新UI
* 每隔一秒执行一次,更新当前播放时间 private fun startTimer() { mTimer = Timer().apply { schedule(object : TimerTask() { override fun run() { //非ui线程不能更新view,所以这里赋值给seek_bar,在seek_bar的事件中去更新 seek_bar.progress = mMediaPlayer!!.currentPosition //tv_currentPosition.text = formatDuration(mMediaPlayer!!.currentPosition) }, 0, 1000)

这里要注意,非ui线程不能更新view,所以这里赋值给seek_bar,在seek_bar的onProgressChanged 回调中去更新。

* 暂停播放 private fun audioPause() { mMediaPlayer?.run { if (this.isPlaying) { pause() cancelTimer()

同样,暂停的时候取消Timer,做到资源及时回收。

取消Timer
    private fun cancelTimer() {
        mTimer?.run {
            cancel()
            mTimer = null

暂停/继续 播放

* 暂停/继续 播放 private fun audioToggle() { mMediaPlayer?.run { if (this.isPlaying) { audioPause() } else { audioStart()

如果只有一个事件触发的话,可以这么来写。

播放器并没有自带restart()方法,不过我们可以手动把播放位置改到初始值,并调用播放。

* 重新播放 private fun audioRestart() { mMediaPlayer?.run { //定位到指定位置,单位毫秒 seekTo(0) audioStart() seek_bar.progress = 0 tv_currentPosition.text = formatDuration(seek_bar!!.progress) //如果是下一首,可以调用reset()重置,然后set新的数据源

如果是下一首,可以调用reset()重置,然后set新的数据源

及时的回收,有利于更好的性能

    override fun onDestroy() {
        mAgentWeb.webLifeCycle.onDestroy()
        super.onDestroy()
        cancelTimer()
        mMediaPlayer?.run {
            stop()
            release()
            mMediaPlayer = null

ok,到此就讲解完了。

写作不易,如果对你有用,点个赞吧 ^ - ^

然后,当用户点击播放按钮时,我们会根据当前播放状态执行相应的操作。当然,实际的音乐播放器可能会更复杂,例如添加播放列表、进度条、音量控制等功能,但这里的示例代码可以作为一个入门点,帮助你开始实现自己的音乐播放器。当然,实际的音乐播放器可能会更复杂,例如添加播放列表、进度条、音量控制等功能,但这里的示例代码可以作为一个入门点,帮助你开始实现自己的音乐播放器。接下来,我们需要在Activity或Fragment中创建一个MediaPlayer对象,并在需要播放音乐的地方进行初始化和控制。 android:requestLegacyExternalStorage=true public class PermissionsManagement { private static final String TAG = PermissionsManagement; public static void requestMyPermissions(Activity 状态图mediastatu.jpg需要注意的是和camera的释放类似,调用release()后没法直接进入idle状态,所以mediaplayer的状态是有问题的,要置null自己的一个栗子:package yunhen.yunjia.aiservice.camera;import android.content.res.AssetFileDescriptor;import android.gr... <br />前段时间在工作中遇到一个问题就是MediaPlayer的播放网络流媒体的时候,当没有完全下载完毕的时,我们调用seekTo的时候会触发OnCompletionListener,你们懂的咯,这样就直接播放下一首了。挺纠结的,所以就决定看看mediaplayer的源码咯,希望能从中找到解决的方法。<br />seekTo在MediaPlayer的调用流程如下图:<br /> <br /><br /> <br />在MediaPlayer.java中的seekTo是一个native修饰的方法<br / android MediaPlaye.getDuration()获取不到在线音乐时长,看看mediaPlayer.getDuration()的源码,里面明确地说了,不支持在线内容,所以,根本原因就是它(不过我本地资源也获取不到) * Gets the duration of the file. * @return the duration in milliseconds, if no duration is available * (fo MediaPlayer必须在合适的状态下调用合适的方法,否则会出现异常,下面列出常见错误信息和说明: 1、E/MediaPlayer(11310): stop called in state 1 调用 stop()之前,MediaPlayer的状态不在【Started, Paused, Prepared or PlaybackCompleted state】范围内。只有在这个范围内的状