Android使用MediaRecorder實(shí)現(xiàn)錄音及播放
現(xiàn)在項(xiàng)目中有使用到音視頻相關(guān)技術(shù),在參考了網(wǎng)上各種大牛的資料及根據(jù)自己項(xiàng)目實(shí)際情況(兼容安卓6.0以上版本動(dòng)態(tài)權(quán)限管理等),把聲音錄制及播放相關(guān)代碼做個(gè)記錄。
public class MediaRecorderActivity extends BaseActivity {
private Button start_tv;
private ListView listView;
//線程操作
private ExecutorService mExecutorService;
//錄音API
private MediaRecorder mMediaRecorder;
//錄音開(kāi)始時(shí)間與結(jié)束時(shí)間
private long startTime, endTime;
//錄音所保存的文件
private File mAudioFile;
//文件列表數(shù)據(jù)
private List<FileBean> dataList;
//錄音文件數(shù)據(jù)列表適配器
private AudioAdapter mAudioAdapter;
//錄音文件保存位置
private String mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/audio/";
//當(dāng)前是否正在播放
private volatile boolean isPlaying;
//播放音頻文件API
private MediaPlayer mediaPlayer;
//使用Handler更新UI線程
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case Constant.RECORD_SUCCESS:
//錄音成功,展示數(shù)據(jù)
if (null == mAudioAdapter) {
mAudioAdapter = new AudioAdapter(MediaRecorderActivity.this, dataList, R.layout.file_item_layout);
}
listView.setAdapter(mAudioAdapter);
break;
//錄音失敗
case Constant.RECORD_FAIL:
showToastMsg(getString(R.string.record_fail));
break;
//錄音時(shí)間太短
case Constant.RECORD_TOO_SHORT:
showToastMsg(getString(R.string.time_too_short));
break;
case Constant.PLAY_COMPLETION:
showToastMsg(getString(R.string.play_over));
break;
case Constant.PLAY_ERROR:
showToastMsg(getString(R.string.play_error));
break;
}
}
};
@Override
protected void setWindowView() {
setContentView(R.layout.activity_record);
//錄音及播放要使用單線程操作
mExecutorService = Executors.newSingleThreadExecutor();
dataList = new ArrayList<>();
}
@Override
protected void initViews() {
this.start_tv = (Button) findViewById(R.id.start_tv);
this.listView = (ListView) findViewById(R.id.listview);
}
@Override
protected void initEvents() {
//類似微信等應(yīng)用按住說(shuō)話進(jìn)行錄音,所以用OnTouch事件
this.start_tv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
//按下操作
case MotionEvent.ACTION_DOWN:
//安卓6.0以上錄音相應(yīng)權(quán)限處理
if (Build.VERSION.SDK_INT > 22) {
permissionForM();
} else {
startRecord();
}
break;
//松開(kāi)操作
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
stopRecord();
break;
}
//對(duì)OnTouch事件做了處理,返回true
return true;
}
});
//點(diǎn)擊播放對(duì)應(yīng)的錄音文件
this.listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
//使用MediaPlayer播放聲音文件
startPlay(dataList.get(i).getFile());
}
});
}
/**
* @description 開(kāi)始進(jìn)行錄音
* @author ldm
* @time 2017/2/9 9:18
*/
private void startRecord() {
start_tv.setText(R.string.stop_by_up);
start_tv.setBackgroundResource(R.drawable.bg_gray_round);
//異步任務(wù)執(zhí)行錄音操作
mExecutorService.submit(new Runnable() {
@Override
public void run() {
//播放前釋放資源
releaseRecorder();
//執(zhí)行錄音操作
recordOperation();
}
});
}
/**
* @description 錄音失敗處理
* @author ldm
* @time 2017/2/9 9:35
*/
private void recordFail() {
mAudioFile = null;
mHandler.sendEmptyMessage(Constant.RECORD_FAIL);
}
/**
* @description 錄音操作
* @author ldm
* @time 2017/2/9 9:34
*/
private void recordOperation() {
//創(chuàng)建MediaRecorder對(duì)象
mMediaRecorder = new MediaRecorder();
//創(chuàng)建錄音文件,.m4a為MPEG-4音頻標(biāo)準(zhǔn)的文件的擴(kuò)展名
mAudioFile = new File(mFilePath + System.currentTimeMillis() + ".m4a");
//創(chuàng)建父文件夾
mAudioFile.getParentFile().mkdirs();
try {
//創(chuàng)建文件
mAudioFile.createNewFile();
//配置mMediaRecorder相應(yīng)參數(shù)
//從麥克風(fēng)采集聲音數(shù)據(jù)
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//設(shè)置保存文件格式為MP4
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
//設(shè)置采樣頻率,44100是所有安卓設(shè)備都支持的頻率,頻率越高,音質(zhì)越好,當(dāng)然文件越大
mMediaRecorder.setAudioSamplingRate(44100);
//設(shè)置聲音數(shù)據(jù)編碼格式,音頻通用格式是AAC
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//設(shè)置編碼頻率
mMediaRecorder.setAudioEncodingBitRate(96000);
//設(shè)置錄音保存的文件
mMediaRecorder.setOutputFile(mAudioFile.getAbsolutePath());
//開(kāi)始錄音
mMediaRecorder.prepare();
mMediaRecorder.start();
//記錄開(kāi)始錄音時(shí)間
startTime = System.currentTimeMillis();
} catch (Exception e) {
e.printStackTrace();
recordFail();
}
}
/**
* @description 結(jié)束錄音操作
* @author ldm
* @time 2017/2/9 9:18
*/
private void stopRecord() {
start_tv.setText(R.string.speak_by_press);
start_tv.setBackgroundResource(R.drawable.bg_white_round);
//停止錄音
mMediaRecorder.stop();
//記錄停止時(shí)間
endTime = System.currentTimeMillis();
//錄音時(shí)間處理,比如只有大于2秒的錄音才算成功
int time = (int) ((endTime - startTime) / 1000);
if (time >= 3) {
//錄音成功,添加數(shù)據(jù)
FileBean bean = new FileBean();
bean.setFile(mAudioFile);
bean.setFileLength(time);
dataList.add(bean);
//錄音成功,發(fā)Message
mHandler.sendEmptyMessage(Constant.RECORD_SUCCESS);
} else {
mAudioFile = null;
mHandler.sendEmptyMessage(Constant.RECORD_TOO_SHORT);
}
//錄音完成釋放資源
releaseRecorder();
}
/**
* @description 翻放錄音相關(guān)資源
* @author ldm
* @time 2017/2/9 9:33
*/
private void releaseRecorder() {
if (null != mMediaRecorder) {
mMediaRecorder.release();
mMediaRecorder = null;
}
}
@Override
public void onClick(View view) {
}
@Override
protected void onDestroy() {
super.onDestroy();
//頁(yè)面銷毀,線程要關(guān)閉
mExecutorService.shutdownNow();
}
/*******6.0以上版本手機(jī)權(quán)限處理***************************/
/**
* @description 兼容手機(jī)6.0權(quán)限管理
* @author ldm
* @time 2016/5/24 14:59
*/
private void permissionForM() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE},
Constant.PERMISSIONS_REQUEST_FOR_AUDIO);
} else {
startRecord();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == Constant.PERMISSIONS_REQUEST_FOR_AUDIO) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startRecord();
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
* @description 播放音頻
* @author ldm
* @time 2017/2/9 16:54
*/
private void playAudio(final File mFile) {
if (null != mFile && !isPlaying) {
isPlaying = true;
mExecutorService.submit(new Runnable() {
@Override
public void run() {
startPlay(mFile);
}
});
}
}
/**
* @description 開(kāi)始播放音頻文件
* @author ldm
* @time 2017/2/9 16:56
*/
private void startPlay(File mFile) {
try {
//初始化播放器
mediaPlayer = new MediaPlayer();
//設(shè)置播放音頻數(shù)據(jù)文件
mediaPlayer.setDataSource(mFile.getAbsolutePath());
//設(shè)置播放監(jiān)聽(tīng)事件
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
//播放完成
playEndOrFail(true);
}
});
//播放發(fā)生錯(cuò)誤監(jiān)聽(tīng)事件
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
playEndOrFail(false);
return true;
}
});
//播放器音量配置
mediaPlayer.setVolume(1, 1);
//是否循環(huán)播放
mediaPlayer.setLooping(false);
//準(zhǔn)備及播放
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
//播放失敗正理
playEndOrFail(false);
}
}
/**
* @description 停止播放或播放失敗處理
* @author ldm
* @time 2017/2/9 16:58
*/
private void playEndOrFail(boolean isEnd) {
isPlaying = false;
if (isEnd) {
mHandler.sendEmptyMessage(Constant.PLAY_COMPLETION);
} else {
mHandler.sendEmptyMessage(Constant.PLAY_ERROR);
}
if (null != mediaPlayer) {
mediaPlayer.setOnCompletionListener(null);
mediaPlayer.setOnErrorListener(null);
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
}
}
}
頁(yè)面布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"> <Button android:id="@+id/start_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="開(kāi)始錄音" android:textSize="16sp" /> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="#d1d1d1" android:dividerHeight="1dp" android:scrollbars="none" android:layout_marginTop="10dp" android:layout_marginBottom="10dp"></ListView> </LinearLayout>
對(duì)應(yīng)資源文件strings.xml:
<resources> <string name="app_name">mediarecorder</string> <string name="record_fail">錄音失敗</string> <string name="time_too_short">時(shí)間太短,請(qǐng)重新錄音</string> <string name="play_over">播放完成</string> <string name="play_error">抱歉,播放發(fā)生異常</string> <string name="stop_by_up">松開(kāi)停止錄音</string> <string name="speak_by_press">按住說(shuō)話</string> <string name="start_record">開(kāi)始錄音</string> <string name="stop_record">停止錄音</string> </resources>
錄音相關(guān)權(quán)限 :
<!--SD卡權(quán)限--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!--錄音權(quán)限--> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
安卓錄制播放音頻:https://github.com/ldm520/Android_Media
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- android MediaRecorder實(shí)現(xiàn)錄屏?xí)r帶錄音功能
- Android編程錄音工具類RecorderUtil定義與用法示例
- Android實(shí)現(xiàn)錄音功能實(shí)現(xiàn)實(shí)例(MediaRecorder)
- Android使用AudioRecord實(shí)現(xiàn)暫停錄音功能實(shí)例代碼
- Android錄音--AudioRecord、MediaRecorder的使用
- android 通過(guò)MediaRecorder實(shí)現(xiàn)簡(jiǎn)單的錄音示例
- Android App調(diào)用MediaRecorder實(shí)現(xiàn)錄音功能的實(shí)例
- Android音頻錄制MediaRecorder之簡(jiǎn)易的錄音軟件實(shí)現(xiàn)代碼
- Android簡(jiǎn)單的利用MediaRecorder進(jìn)行錄音的實(shí)例代碼
- Android使用AudioRecord實(shí)現(xiàn)錄音功能
相關(guān)文章
實(shí)現(xiàn)qq中按返回鍵返回桌面不退出程序的實(shí)例
下面小編就為大家?guī)?lái)一篇實(shí)現(xiàn)qq中按返回鍵返回桌面不退出程序的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
Android中通過(guò)訪問(wèn)本地相冊(cè)或者相機(jī)設(shè)置用戶頭像實(shí)例
本篇文章主要介紹了Android中通過(guò)訪問(wèn)本地相冊(cè)或者相機(jī)設(shè)置用戶頭像,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
Android編程學(xué)習(xí)之抽象類AbsListView用法實(shí)例分析
這篇文章主要介紹了Android編程學(xué)習(xí)之抽象類AbsListView用法,較為詳細(xì)的分析了抽象類AbsListView的功能、結(jié)構(gòu)、定義及使用注意事項(xiàng)等,需要的朋友可以參考下2015-10-10
使用RecyclerView實(shí)現(xiàn)瀑布流高度自適應(yīng)
這篇文章主要為大家詳細(xì)介紹了使用RecyclerView實(shí)現(xiàn)瀑布流高度自適應(yīng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Android中AOP的應(yīng)用實(shí)踐之過(guò)濾重復(fù)點(diǎn)擊
這篇文章主要給大家介紹了關(guān)于Android中AOP的應(yīng)用實(shí)踐之過(guò)濾重復(fù)點(diǎn)擊的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09
Android 中不用線程如何實(shí)現(xiàn)倒計(jì)時(shí)
本文給大家分享android中不用線程如何實(shí)現(xiàn)倒計(jì)時(shí)功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01

