以前學(xué)了很多東西,都沒(méi)怎么做記錄腹泌,慢慢的時(shí)間長(zhǎng)了也就給忘了嘶卧,當(dāng)初踩過(guò)的坑翻來(lái)覆去的踩,由于這些血淋林的教訓(xùn)凉袱,所以從現(xiàn)在開(kāi)始脸候,每次學(xué)了新的東西都要來(lái)寫個(gè)博客做個(gè)記錄總結(jié)一下了。
最近在學(xué)習(xí)Android多媒體這一塊绑蔫,現(xiàn)在剛好學(xué)到了MediaRecorder和MediaPlayer。今天也就介紹下這兩個(gè)類吧泵额,也有利于以后初學(xué)者的學(xué)習(xí)配深。
本文一共分3個(gè)部分:第一和第二部分用來(lái)介紹這兩個(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è)類是用來(lái)去錄制音頻和視頻的篓叶。錄制基于一種簡(jiǎn)單的狀態(tài)機(jī)制。
這個(gè)狀態(tài)機(jī)制是什么呢羞秤?Google給了一張圖
從圖中可以看出MediaRecorder 有這么幾種狀態(tài):
Initial:初始化缸托。也就是MediaRecorder 剛被創(chuàng)建的時(shí)候。在這個(gè)時(shí)候我們?nèi)ピO(shè)置音頻或者視頻的來(lái)源了瘾蛋,可能這個(gè)時(shí)候就有人問(wèn)了 音頻或者視頻的來(lái)源是什么意思俐镐,舉個(gè)例子吧,比如當(dāng)我們?cè)阡浺舻臅r(shí)候哺哼,這個(gè)聲音的來(lái)源就可以設(shè)置成手機(jī)的麥克風(fēng)佩抹。
Initialized:初始化完成叼风。這里我們已經(jīng)知道音頻或者視頻的來(lái)源了,在這里我們就可以設(shè)置一些輸出的屬性了棍苹,比如輸出文件的保存格式无宿,編碼什么的。由于水平有限還有很多關(guān)于媒體文件屬性枢里,我也不是太理解孽鸡。
DataSourceConfigured:數(shù)據(jù)源配置改變。也就是我們改變了一些輸出的屬性栏豺,就會(huì)進(jìn)入到這個(gè)狀態(tài)彬碱。
Prepered:處于這個(gè)狀態(tài)就說(shuō)明了我們的配置已經(jīng)完成了,現(xiàn)在就等去錄制了冰悠。
Recordeing:這個(gè)不用我說(shuō)也應(yīng)該明白了堡妒。
Released:資源被釋放了。
Error:錄制的時(shí)候發(fā)生了錯(cuò)誤溉卓。
圖中已經(jīng)把狀態(tài)之間是怎么切換的已經(jīng)描述的非常清楚了皮迟,我也不過(guò)多介紹了。
2.MediaPlayer
關(guān)于MediaPlayer官網(wǎng)給的解釋是:
MediaPlayer class can be used to control playback of audio/video files and streams.
簡(jiǎn)單翻譯過(guò)來(lái)就是:這個(gè)類是用來(lái)去播放音頻桑寨,視頻文件和流文件的伏尼。
他也對(duì)應(yīng)了一個(gè)狀態(tài)圖:
其實(shí)他的大致流程和MediaRecorder差不多,這里我就略過(guò)了尉尾。
你以為到這就結(jié)束了嗎爆阶?肯定不是的,上面的也就為下面我要做的做個(gè)鋪墊而已沙咏,有了上面的基礎(chǔ)辨图,后面的肯定就輕松了。
3.做一個(gè)錄音的小Demo
先來(lái)看一看效果圖吧
看起來(lái)還是很簡(jiǎn)單吧肢藐。
開(kāi)始是初始狀態(tài)故河,然后是錄制狀態(tài)(中間有個(gè)波浪形的自定義控件),在這個(gè)狀態(tài)下會(huì)顯示當(dāng)前的錄制時(shí)長(zhǎng)吆豹,波浪形控件會(huì)顯示鱼的,最后就是錄制完成,可以點(diǎn)擊播放了痘煤。
最下面是保存和刪除按鈕凑阶,可以刪除當(dāng)前的錄音。PS:保存按鈕沒(méi)有任何實(shí)現(xiàn)衷快,錄音文件會(huì)自動(dòng)保存宙橱。
知道了這個(gè)效果現(xiàn)在就來(lái)介紹是怎么做的吧。
這個(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();
}
/**
* 開(kāi)始錄音
*/
public void startRecord() {
try {
mMediaRecorder = new MediaRecorder();
File file = new File(mSavePath, generateFileName());
mCurrentFilePath = file.getAbsolutePath();
// 設(shè)置錄音文件的保存位置
mMediaRecorder.setOutputFile(mCurrentFilePath);
// 設(shè)置錄音的來(lái)源(從哪里錄音)
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)多余了。如果還是又不懂或者有問(wèn)題需要提出的呕乎,歡迎留言积担。
到這里我們的核心東西已經(jīng)準(zhǔn)備好了,現(xiàn)在就剩下一個(gè)Activity讓我們?nèi)?shí)現(xiàn)剛才效果圖上的小Demo了猬仁。
我們來(lái)分析一下我們的小Demo會(huì)有哪些狀態(tài):
1.正常狀態(tài)帝璧,也就是沒(méi)有錄制的狀態(tài)
2.錄制中
3.錄制完成
4.播放狀態(tài)
5.暫停狀態(tài)
對(duì)于這幾種狀態(tài)我定義了對(duì)應(yīng)的五個(gè)常量來(lái)表示。
<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)來(lá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);
//開(kāi)始錄音
mMediaRecorderHelper.startRecord();
mWaveView.setVisibility(View.VISIBLE);
mIsRecorder = true;
//開(kāi)啟計(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
如果在閱讀的過(guò)程中又發(fā)現(xiàn)錯(cuò)誤渴庆,也歡迎留言糾錯(cuò)。