Android — 使用MediaPlayer进行音频播放

上一篇说到音频录制的时候我就知道肯定会有音频播放这个功能,果不其然后续播放的需求又给我加上了,因为之前是AMR格式,所以这里使用MediaPlayer进行播放,据ios说播放amr格式的音频还挺麻烦的。

一、添加必要权限

//有播放网络资源的需要添加网络权限。
 <uses-permission android:name="android.permission.INTERNET"/> 
//播放本地资源需要添加SD卡权限,一般存储我都会全加上。 
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 
//在播放时有特殊设置的需要声明一个相应的WAKE_LOCK权限 
<uses-permission android:name="android.permission.WAKE_LOCK"/>

二、MediaPlayer 介绍

1.状态图

音频/视频文件和流的播放控制作为状态机进行管理。下图显示了受支持的播放控制操作驱动的 MediaPlayer 对象的生命周期和状态。椭圆形表示 MediaPlayer 对象可能驻留的状态。弧形表示驱动对象状态转换的回放控制操作。有两种类型的弧。具有单箭头的弧表示同步方法调用,而具有双箭头的弧表示异步方法调用。

MediaPlayer状态图

具体信息可以去Android官网阅读

2.MediaPlayer公开方法

方法有很多,这里只说常用的几个方法,其他的去Android官网查阅。

公开方法
static MediaPlayer create(Context context, int resid )为给定资源ID创建MediaPlayer的便捷方法,一般播放Raw下资源会使用此方法。
void setDataSource (AssetFileDescriptor afd)
setDataSource (FileDescriptor fd)
setDataSource (String path)
setDataSource (MediaDataSource dataSource)
setDataSource (Context context, Uri uri)

设置数据源,这里可以设置 AssetFileDescriptor 、 FileDescriptor 、 文件路径或http / rtsp URL 、 MediaDataSource 、 Uri
void prepare()
同步准备播放器以进行播放。
void prepareAsync()
准备播放器异步播放。
void pause()
暂停播放。
void release()
释放与此MediaPlayer对象关联的资源。
void reset()
将MediaPlayer重置为其未初始化状态。
void start()
开始或继续播放。
void stop()
开始或暂停播放后停止播放。
void setVolume(float leftVolume, float rightVolume)
设置播放的音量,左右声道取值 0~1。
void setOnCompletionListener(MediaPlayer. OnCompletionListener listener)
播放完成后的回调。
void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener)
网络流缓冲区的状态更改时的回调。
void setOnErrorListener(MediaPlayer.OnErrorListener listener)
错误回调。
void setOnInfoListener(MediaPlayer.OnInfoListener listener)
注册一个在信息/警告可用时要调用的回调。
void setOnPreparedListener(MediaPlayer.OnPreparedListener listener)
注册当媒体源准备好播放时要调用的回调。
void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener)
指定播放位置后的回调。
boolean isLooping()
检查MediaPlayer是循环还是非循环。
boolean isPlaying()
检查MediaPlayer是否正在播放。
void seekTo(long msec)
播放指定的时间位置。
void setScreenOnWhilePlaying(boolean screenOn)
控制在播放时是否熄屏。

三、代码编写

这里有个需要注意的地方:一般加载比较小的资源时候可以使用prepare(),但是如果你加载资源比较大的或者是网络资源的时候就是一个耗时操作,我们都知道不能在主线程执行耗时操作,所以就需要使用prepareAsync()配合setOnPreparedListener()在准备完成后自动播放音频。
如果你不信这个邪你可以用prepare()加载网络资源试试。


/**
 * 设置播放类型,之后调用stop方便切换类型。
 */
 
  private int PLAY_STATE = -1;//判断是什么类型
  public static final int PLAY_STATE0 = 1;//文件
  public static final int PLAY_STATE1 = 2;//raw
  public static final int PLAY_STATE2 = 3;//assets
  public static final int PLAY_STATE3 = 4;//网络
 
  private MediaPlayer mMediaPlayer = null;
 
  //设置不同的回调
  private MediaPlayFunctionListener mediaPlayFunctionListener;
  private MediaPlayInfoListener mMediaPlayInfoListener;
 
  //设置文件路径
  public void setFilePlay(File file) {
    this.targetFile = file;
    PLAY_STATE = PLAY_STATE0;
    stop();
  }
  //设置Raw播放
  public void setRawPlay(Context context,int rawId){
    this.mContext = context;
    this.rawId = rawId;
    PLAY_STATE = PLAY_STATE1;
    stop();
  }
  //设置Assets播放
  public void setAssetsName(Context context,String assetsName) {
    this.mContext = context;
    this.assetsName = assetsName;
    PLAY_STATE = PLAY_STATE2;
    stop();
  }
  //设置网络资源播放
  public void setNetPath(String netPath) {
    this.netPath = netPath;
    PLAY_STATE = PLAY_STATE3;
    stop();
  }
 
  //开始播放
  public boolean start() {
    if (PLAY_STATE == PLAY_STATE1){
      mMediaPlayer = MediaPlayer.create(mContext,rawId);
    }else {
      mMediaPlayer = new MediaPlayer();
    }
    try {
      switch (PLAY_STATE){
        case PLAY_STATE0://本地文件为数据源传入路径
          mMediaPlayer.setDataSource(targetFile.getAbsolutePath());
          mMediaPlayer.prepare();
          break;
        case PLAY_STATE1://如果raw为数据源则不处理
          break;
        case PLAY_STATE2://Assets下文件为数据源
          AssetFileDescriptor fileDescriptor = mContext.getAssets().openFd(assetsName);
          mMediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(),fileDescriptor.getLength());
          mMediaPlayer.prepare();
          break;
        case PLAY_STATE3://网络数据源 设置网络连接
          mMediaPlayer.setDataSource(netPath);
          mMediaPlayer.prepareAsync();
          break;
      }
      //播放完成自动停止
      mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override public void onCompletion(MediaPlayer mediaPlayer) {
          //停止播放
          mediaPlayer.stop();
          //回调停止播放的方法。
          if (mediaPlayFunctionListener != null)
            mediaPlayFunctionListener.stop();
          //回调播放完成的方法
          if (mMediaPlayInfoListener != null)
            mMediaPlayInfoListener.onCompletion(mediaPlayer);
        }
      });
      //准备完毕 自动播放
      mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override public void onPrepared(MediaPlayer mediaPlayer) {
          mediaPlayer.start();
          //准备完毕,回调准备和开始方法
          if (mediaPlayFunctionListener != null) {
            mediaPlayFunctionListener.prepared();
            mediaPlayFunctionListener.start();
          }
        }
      });
      //播放错误监听
      mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
        @Override public boolean onError(MediaPlayer mp, int what, int extra) {
          //回调错误方法
          if (mMediaPlayInfoListener != null) {
            mMediaPlayInfoListener.onError(mp, what, extra);
          }
          //回调停止方法
          if (mMediaPlayer != null)
            stop();
          return false;
        }
      });
      //网络缓冲监听
      mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
        @Override public void onBufferingUpdate(MediaPlayer mp, int percent) {
          //回调网络缓冲方法
          if (mMediaPlayInfoListener != null)
            mMediaPlayInfoListener.onBufferingUpdate(mp,percent);
        }
      });
      //调整进度监听
      mMediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
        @Override public void onSeekComplete(MediaPlayer mp) {
          //回调调整进度后的方法,这里只有调整播放位置后才会调用
          if (mMediaPlayInfoListener != null)
            mMediaPlayInfoListener.onSeekComplete(mp);
        }
      });
    } catch (IOException e) {
      e.printStackTrace();
      mMediaPlayer.stop();
      mMediaPlayer.reset();
      mMediaPlayer.release();
      mMediaPlayer = null;
    }
    boolean result = (mMediaPlayer != null);
    this.isPlaying = result;
    return result;
  }
//停止播放
  public void stop() {
    this.isPlaying = false;
    this.duration = 0;
 
    if (mMediaPlayer != null) {
      mHandler.removeCallbacks(mRunnable);
      mMediaPlayer.stop();
      mMediaPlayer.reset();
      if (mediaPlayFunctionListener != null)
        mediaPlayFunctionListener.reset();
      mMediaPlayer.release();
      mMediaPlayer = null;
    }
  }
 
  //resume调用 继续播放也调用此方法即可
  public void resume() {
    if (mMediaPlayer != null) {
      this.isPlaying = true;
      mHandler.postDelayed(mRunnable,sleep);
      mMediaPlayer.start();
    }
  }
 
  //暂停
  public void pause() {
    this.isPlaying = false;
    mHandler.removeCallbacks(mRunnable);
    if (mMediaPlayer != null) {
      mMediaPlayer.pause();
      if (mediaPlayFunctionListener != null)
        mediaPlayFunctionListener.pause();
    }
  }

我已经把MediaPlayer写成工具类上传到Github中具体的使用方法可以查看Wiki,如果你想直接使用导入依赖即可,如果你不想去Github想直接修改或者自定义其他方法直接下载 mediaplaylib (提取码: xnv5) 即可

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341