Android — 使用MediaPlayer進行音頻播放

上一篇說到音頻錄制的時候我就知道肯定會有音頻播放這個功能,果不其然后續(xù)播放的需求又給我加上了掂林,因為之前是AMR格式进宝,所以這里使用MediaPlayer進行播放恨搓,據(jù)ios說播放amr格式的音頻還挺麻煩的部逮。

一、添加必要權限

//有播放網(wǎng)絡資源的需要添加網(wǎng)絡權限嫂易。
 <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.狀態(tài)圖

音頻/視頻文件和流的播放控制作為狀態(tài)機進行管理颅和。下圖顯示了受支持的播放控制操作驅動的 MediaPlayer 對象的生命周期和狀態(tài)傅事。橢圓形表示 MediaPlayer 對象可能駐留的狀態(tài)∠坷弧形表示驅動對象狀態(tài)轉換的回放控制操作蹭越。有兩種類型的弧。具有單箭頭的弧表示同步方法調(diào)用教届,而具有雙箭頭的弧表示異步方法調(diào)用响鹃。

MediaPlayer狀態(tài)圖

具體信息可以去Android官網(wǎng)閱讀

2.MediaPlayer公開方法

方法有很多,這里只說常用的幾個方法案训,其他的去Android官網(wǎng)查閱买置。

公開方法
static MediaPlayer create(Context context, int resid )為給定資源ID創(chuàng)建MediaPlayer的便捷方法,一般播放Raw下資源會使用此方法强霎。
void setDataSource (AssetFileDescriptor afd)
setDataSource (FileDescriptor fd)
setDataSource (String path)
setDataSource (MediaDataSource dataSource)
setDataSource (Context context, Uri uri)

設置數(shù)據(jù)源忿项,這里可以設置 AssetFileDescriptor 、 FileDescriptor 城舞、 文件路徑或http / rtsp URL 轩触、 MediaDataSource 、 Uri
void prepare()
同步準備播放器以進行播放家夺。
void prepareAsync()
準備播放器異步播放脱柱。
void pause()
暫停播放。
void release()
釋放與此MediaPlayer對象關聯(lián)的資源秦踪。
void reset()
將MediaPlayer重置為其未初始化狀態(tài)褐捻。
void start()
開始或繼續(xù)播放。
void stop()
開始或暫停播放后停止播放椅邓。
void setVolume(float leftVolume, float rightVolume)
設置播放的音量柠逞,左右聲道取值 0~1。
void setOnCompletionListener(MediaPlayer. OnCompletionListener listener)
播放完成后的回調(diào)景馁。
void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener)
網(wǎng)絡流緩沖區(qū)的狀態(tài)更改時的回調(diào)板壮。
void setOnErrorListener(MediaPlayer.OnErrorListener listener)
錯誤回調(diào)。
void setOnInfoListener(MediaPlayer.OnInfoListener listener)
注冊一個在信息/警告可用時要調(diào)用的回調(diào)合住。
void setOnPreparedListener(MediaPlayer.OnPreparedListener listener)
注冊當媒體源準備好播放時要調(diào)用的回調(diào)绰精。
void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener)
指定播放位置后的回調(diào)。
boolean isLooping()
檢查MediaPlayer是循環(huán)還是非循環(huán)透葛。
boolean isPlaying()
檢查MediaPlayer是否正在播放笨使。
void seekTo(long msec)
播放指定的時間位置。
void setScreenOnWhilePlaying(boolean screenOn)
控制在播放時是否熄屏僚害。

三硫椰、代碼編寫

這里有個需要注意的地方:一般加載比較小的資源時候可以使用prepare(),但是如果你加載資源比較大的或者是網(wǎng)絡資源的時候就是一個耗時操作,我們都知道不能在主線程執(zhí)行耗時操作靶草,所以就需要使用prepareAsync()配合setOnPreparedListener()在準備完成后自動播放音頻蹄胰。
如果你不信這個邪你可以用prepare()加載網(wǎng)絡資源試試。


/**
 * 設置播放類型奕翔,之后調(diào)用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;//網(wǎng)絡
 
  private MediaPlayer mMediaPlayer = null;
 
  //設置不同的回調(diào)
  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();
  }
  //設置網(wǎng)絡資源播放
  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://本地文件為數(shù)據(jù)源傳入路徑
          mMediaPlayer.setDataSource(targetFile.getAbsolutePath());
          mMediaPlayer.prepare();
          break;
        case PLAY_STATE1://如果raw為數(shù)據(jù)源則不處理
          break;
        case PLAY_STATE2://Assets下文件為數(shù)據(jù)源
          AssetFileDescriptor fileDescriptor = mContext.getAssets().openFd(assetsName);
          mMediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(),fileDescriptor.getLength());
          mMediaPlayer.prepare();
          break;
        case PLAY_STATE3://網(wǎng)絡數(shù)據(jù)源 設置網(wǎng)絡連接
          mMediaPlayer.setDataSource(netPath);
          mMediaPlayer.prepareAsync();
          break;
      }
      //播放完成自動停止
      mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override public void onCompletion(MediaPlayer mediaPlayer) {
          //停止播放
          mediaPlayer.stop();
          //回調(diào)停止播放的方法。
          if (mediaPlayFunctionListener != null)
            mediaPlayFunctionListener.stop();
          //回調(diào)播放完成的方法
          if (mMediaPlayInfoListener != null)
            mMediaPlayInfoListener.onCompletion(mediaPlayer);
        }
      });
      //準備完畢 自動播放
      mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override public void onPrepared(MediaPlayer mediaPlayer) {
          mediaPlayer.start();
          //準備完畢派继,回調(diào)準備和開始方法
          if (mediaPlayFunctionListener != null) {
            mediaPlayFunctionListener.prepared();
            mediaPlayFunctionListener.start();
          }
        }
      });
      //播放錯誤監(jiān)聽
      mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
        @Override public boolean onError(MediaPlayer mp, int what, int extra) {
          //回調(diào)錯誤方法
          if (mMediaPlayInfoListener != null) {
            mMediaPlayInfoListener.onError(mp, what, extra);
          }
          //回調(diào)停止方法
          if (mMediaPlayer != null)
            stop();
          return false;
        }
      });
      //網(wǎng)絡緩沖監(jiān)聽
      mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
        @Override public void onBufferingUpdate(MediaPlayer mp, int percent) {
          //回調(diào)網(wǎng)絡緩沖方法
          if (mMediaPlayInfoListener != null)
            mMediaPlayInfoListener.onBufferingUpdate(mp,percent);
        }
      });
      //調(diào)整進度監(jiān)聽
      mMediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
        @Override public void onSeekComplete(MediaPlayer mp) {
          //回調(diào)調(diào)整進度后的方法宾袜,這里只有調(diào)整播放位置后才會調(diào)用
          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調(diào)用 繼續(xù)播放也調(diào)用此方法即可
  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();
    }
  }

我已經(jīng)把MediaPlayer寫成工具類上傳到Github中具體的使用方法可以查看Wiki,如果你想直接使用導入依賴即可互艾,如果你不想去Github想直接修改或者自定義其他方法直接下載 mediaplaylib (提取碼: xnv5) 即可

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末试和,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子纫普,更是在濱河造成了極大的恐慌阅悍,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昨稼,死亡現(xiàn)場離奇詭異节视,居然都是意外死亡,警方通過查閱死者的電腦和手機假栓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門寻行,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人匾荆,你說我怎么就攤上這事拌蜘。” “怎么了牙丽?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵简卧,是天一觀的道長。 經(jīng)常有香客問我烤芦,道長举娩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任构罗,我火速辦了婚禮铜涉,結果婚禮上,老公的妹妹穿的比我還像新娘遂唧。我一直安慰自己芙代,他們只是感情好,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布盖彭。 她就那樣靜靜地躺著链蕊,像睡著了一般事甜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上滔韵,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機與錄音掌实,去河邊找鬼陪蜻。 笑死,一個胖子當著我的面吹牛贱鼻,可吹牛的內(nèi)容都是我干的宴卖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼邻悬,長吁一口氣:“原來是場噩夢啊……” “哼症昏!你這毒婦竟也來了?” 一聲冷哼從身側響起父丰,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤肝谭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蛾扇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體攘烛,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年镀首,在試婚紗的時候發(fā)現(xiàn)自己被綠了坟漱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡更哄,死狀恐怖芋齿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情成翩,我是刑警寧澤觅捆,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站捕传,受9級特大地震影響惠拭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庸论,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一职辅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聂示,春花似錦域携、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趋观。三九已至,卻和暖如春锋边,著一層夾襖步出監(jiān)牢的瞬間皱坛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工豆巨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剩辟,地道東北人。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓往扔,卻偏偏與公主長得像贩猎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子萍膛,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349