以前學(xué)了很多東西,都沒怎么做記錄,慢慢的時(shí)間長了也就給忘了,當(dāng)初踩過的坑翻來覆去的踩,由于這些血淋林的教訓(xùn),所以從現(xiàn)在開始,每次學(xué)了新的東西都要來寫個(gè)博客做個(gè)記錄總結(jié)一下了。
最近在學(xué)習(xí)Android多媒體這一塊,現(xiàn)在剛好學(xué)到了MediaRecorder和MediaPlayer。今天也就介紹下這兩個(gè)類吧,也有利于以后初學(xué)者的學(xué)習(xí)。
本文一共分3個(gè)部分:第一和第二部分用來介紹這兩個(gè)類,第3部分做一個(gè)錄音的小Demo(附源碼)
1.MediaRecorder
關(guān)于MediaRecorder 官網(wǎng)給的解釋是
Used to record audio and video. The recording control is based on a simple state machine
大致意思就是,這個(gè)類是用來去錄制音頻和視頻的。錄制基于一種簡單的狀態(tài)機(jī)制。
這個(gè)狀態(tài)機(jī)制是什么呢?Google給了一張圖
從圖中可以看出MediaRecorder 有這么幾種狀態(tài):
Initial:初始化。也就是MediaRecorder 剛被創(chuàng)建的時(shí)候。在這個(gè)時(shí)候我們?nèi)ピO(shè)置音頻或者視頻的來源了,可能這個(gè)時(shí)候就有人問了 音頻或者視頻的來源是什么意思,舉個(gè)例子吧,比如當(dāng)我們?cè)阡浺舻臅r(shí)候,這個(gè)聲音的來源就可以設(shè)置成手機(jī)的麥克風(fēng)。
Initialized:初始化完成。這里我們已經(jīng)知道音頻或者視頻的來源了,在這里我們就可以設(shè)置一些輸出的屬性了,比如輸出文件的保存格式,編碼什么的。由于水平有限還有很多關(guān)于媒體文件屬性,我也不是太理解。
DataSourceConfigured:數(shù)據(jù)源配置改變。也就是我們改變了一些輸出的屬性,就會(huì)進(jìn)入到這個(gè)狀態(tài)。
Prepered:處于這個(gè)狀態(tài)就說明了我們的配置已經(jīng)完成了,現(xiàn)在就等去錄制了。
Recordeing:這個(gè)不用我說也應(yīng)該明白了。
Released:資源被釋放了。
Error:錄制的時(shí)候發(fā)生了錯(cuò)誤。
圖中已經(jīng)把狀態(tài)之間是怎么切換的已經(jīng)描述的非常清楚了,我也不過多介紹了。
2.MediaPlayer
關(guān)于MediaPlayer官網(wǎng)給的解釋是:
MediaPlayer class can be used to control playback of audio/video files and streams.
簡單翻譯過來就是:這個(gè)類是用來去播放音頻,視頻文件和流文件的。
他也對(duì)應(yīng)了一個(gè)狀態(tài)圖:
其實(shí)他的大致流程和MediaRecorder差不多,這里我就略過了。
你以為到這就結(jié)束了嗎?肯定不是的,上面的也就為下面我要做的做個(gè)鋪墊而已,有了上面的基礎(chǔ),后面的肯定就輕松了。
3.做一個(gè)錄音的小Demo
先來看一看效果圖吧
看起來還是很簡單吧。
開始是初始狀態(tài),然后是錄制狀態(tài)(中間有個(gè)波浪形的自定義控件),在這個(gè)狀態(tài)下會(huì)顯示當(dāng)前的錄制時(shí)長,波浪形控件會(huì)顯示,最后就是錄制完成,可以點(diǎn)擊播放了。
最下面是保存和刪除按鈕,可以刪除當(dāng)前的錄音。PS:保存按鈕沒有任何實(shí)現(xiàn),錄音文件會(huì)自動(dòng)保存。
知道了這個(gè)效果現(xiàn)在就來介紹是怎么做的吧。
這個(gè)小Demo我對(duì)MediaRecorder和MediaPlayer做了一下封裝,方便以后使用調(diào)用方便。
MediaRecorderHelper代碼如下:
<pre>
public class MediaRecorderHelper {
private MediaRecorder mMediaRecorder;
private String mSavePath;
private String mCurrentFilePath;
public MediaRecorderHelper(String savePath) {
mSavePath = savePath;
File file = new File(mSavePath);
if (!file.exists()) file.mkdirs();
}
/**
* 開始錄音
*/
public void startRecord() {
try {
mMediaRecorder = new MediaRecorder();
File file = new File(mSavePath, generateFileName());
mCurrentFilePath = file.getAbsolutePath();
// 設(shè)置錄音文件的保存位置
mMediaRecorder.setOutputFile(mCurrentFilePath);
// 設(shè)置錄音的來源(從哪里錄音)
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 設(shè)置錄音的保存格式
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
// 設(shè)置錄音的編碼
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 停止錄音
*/
public void stopAndRelease() {
if (mMediaRecorder == null) return;
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
}
/***
* 取消本次錄音操作
*/
public void cancel() {
this.stopAndRelease();
if (mCurrentFilePath != null) {
File file = new File(mCurrentFilePath);
file.delete();
mCurrentFilePath = null;
}
}
private String generateFileName() {
return UUID.randomUUID().toString() + ".amr";
}
/**
* 得到錄音文件的路徑
*
* @return
*/
public String getCurrentFilePath() {
return mCurrentFilePath;
}
}
</pre>
MediaPlayerHelper代碼如下:
<pre>
public class MediaPlayerHelper {
private static MediaPlayer mMediaPlayer;
private static boolean isPause = false;
public static void playSound(String filePath) {
playSound(filePath, null);
}
public static void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mMediaPlayer.reset();
return false;
}
});
} else {
mMediaPlayer.reset();
}
try {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
if (onCompletionListener != null) {
mMediaPlayer.setOnCompletionListener(onCompletionListener);
}
mMediaPlayer.setDataSource(filePath);
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 暫停播放
*/
public static void pause() {
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
isPause = true;
}
}
/**
* 繼續(xù)播放
*/
public static void resume() {
if (mMediaPlayer != null && isPause) {
mMediaPlayer.start();
isPause = false;
}
}
/**
* 釋放資源
*/
public static void realese() {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
isPause = true;
}
}
}
</pre>
這兩個(gè)類的代碼注釋的已經(jīng)非常詳細(xì)了,我再在這里介紹也就顯得有點(diǎn)多余了。如果還是又不懂或者有問題需要提出的,歡迎留言。
到這里我們的核心東西已經(jīng)準(zhǔn)備好了,現(xiàn)在就剩下一個(gè)Activity讓我們?nèi)?shí)現(xiàn)剛才效果圖上的小Demo了。
我們來分析一下我們的小Demo會(huì)有哪些狀態(tài):
1.正常狀態(tài),也就是沒有錄制的狀態(tài)
2.錄制中
3.錄制完成
4.播放狀態(tài)
5.暫停狀態(tài)
對(duì)于這幾種狀態(tài)我定義了對(duì)應(yīng)的五個(gè)常量來表示。
<pre>
private static final int ACTION_NORMAL = 0;
private static final int ACTION_RECORDING = 1;
private static final int ACTION_COMMPLETE = 2;
private static final int ACTION_PLAYING = 3;
private static final int ACTION_PAUSE = 4;
</pre>
在中間的錄制按鈕在點(diǎn)擊的時(shí)候,我們要根據(jù)當(dāng)前的狀態(tài)來進(jìn)行狀態(tài)之間的切換:
依然是附代碼,就是這么任性~~~~
<pre>
/**
* 切換ACTION狀態(tài)
*/
private void switchActionState() {
mIsRecorder = false;
if (mCurrentActionState == ACTION_NORMAL) {
mCurrentActionState = ACTION_RECORDING;
mIvAction.setImageResource(R.drawable.pause);
//開始錄音
mMediaRecorderHelper.startRecord();
mWaveView.setVisibility(View.VISIBLE);
mIsRecorder = true;
//開啟計(jì)時(shí)線程
mCountTimeThread = new Thread(this);
mCountTimeThread.start();
} else if (mCurrentActionState == ACTION_RECORDING) {//錄制中
mCurrentActionState = ACTION_COMMPLETE;
mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded);
//停止錄音
mMediaRecorderHelper.stopAndRelease();
mRlBottom.setVisibility(View.VISIBLE);
mWaveView.setVisibility(View.INVISIBLE);
} else if (mCurrentActionState == ACTION_COMMPLETE) {//錄制完成
mCurrentActionState = ACTION_PLAYING;
mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded_play);
//播放錄音
MediaPlayerHelper.playSound(mMediaRecorderHelper.getCurrentFilePath(), new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//當(dāng)播放完了之后切換到錄制完成的狀態(tài)
mCurrentActionState = ACTION_COMMPLETE;
mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded);
}
});
} else if (mCurrentActionState == ACTION_PLAYING) {//播放中
mCurrentActionState = ACTION_PAUSE;
mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded);
//暫停播放
MediaPlayerHelper.pause();
} else if (mCurrentActionState == ACTION_PAUSE) {//暫停
mCurrentActionState = ACTION_PLAYING;
mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded_play);
//繼續(xù)播放
MediaPlayerHelper.resume();
}
}
</pre>
上面的就是整個(gè)Demo的核心思想。
需要源碼的可以點(diǎn)擊下面的鏈接下載。
MediaRecorderDemo
如果在閱讀的過程中又發(fā)現(xiàn)錯(cuò)誤,也歡迎留言糾錯(cuò)。