Android使用MediaRecorder实现录音及播放
作者:迟做总比不做强 发布时间:2021-09-10 00:46:41
标签:Android,MediaRecorder,录音
现在项目中有使用到音视频相关技术,在参考了网上各种大牛的资料及根据自己项目实际情况(兼容安卓6.0以上版本动态权限管理等),把声音录制及播放相关代码做个记录。
public class MediaRecorderActivity extends BaseActivity {
private Button start_tv;
private ListView listView;
//线程操作
private ExecutorService mExecutorService;
//录音API
private MediaRecorder mMediaRecorder;
//录音开始时间与结束时间
private long startTime, endTime;
//录音所保存的文件
private File mAudioFile;
//文件列表数据
private List<FileBean> dataList;
//录音文件数据列表适配器
private AudioAdapter mAudioAdapter;
//录音文件保存位置
private String mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/audio/";
//当前是否正在播放
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:
//录音成功,展示数据
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;
//录音时间太短
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() {
//类似微信等应用按住说话进行录音,所以用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以上录音相应权限处理
if (Build.VERSION.SDK_INT > 22) {
permissionForM();
} else {
startRecord();
}
break;
//松开操作
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
stopRecord();
break;
}
//对OnTouch事件做了处理,返回true
return true;
}
});
//点击播放对应的录音文件
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 开始进行录音
* @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);
//异步任务执行录音操作
mExecutorService.submit(new Runnable() {
@Override
public void run() {
//播放前释放资源
releaseRecorder();
//执行录音操作
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() {
//创建MediaRecorder对象
mMediaRecorder = new MediaRecorder();
//创建录音文件,.m4a为MPEG-4音频标准的文件的扩展名
mAudioFile = new File(mFilePath + System.currentTimeMillis() + ".m4a");
//创建父文件夹
mAudioFile.getParentFile().mkdirs();
try {
//创建文件
mAudioFile.createNewFile();
//配置mMediaRecorder相应参数
//从麦克风采集声音数据
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置保存文件格式为MP4
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
//设置采样频率,44100是所有安卓设备都支持的频率,频率越高,音质越好,当然文件越大
mMediaRecorder.setAudioSamplingRate(44100);
//设置声音数据编码格式,音频通用格式是AAC
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//设置编码频率
mMediaRecorder.setAudioEncodingBitRate(96000);
//设置录音保存的文件
mMediaRecorder.setOutputFile(mAudioFile.getAbsolutePath());
//开始录音
mMediaRecorder.prepare();
mMediaRecorder.start();
//记录开始录音时间
startTime = System.currentTimeMillis();
} catch (Exception e) {
e.printStackTrace();
recordFail();
}
}
/**
* @description 结束录音操作
* @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();
//记录停止时间
endTime = System.currentTimeMillis();
//录音时间处理,比如只有大于2秒的录音才算成功
int time = (int) ((endTime - startTime) / 1000);
if (time >= 3) {
//录音成功,添加数据
FileBean bean = new FileBean();
bean.setFile(mAudioFile);
bean.setFileLength(time);
dataList.add(bean);
//录音成功,发Message
mHandler.sendEmptyMessage(Constant.RECORD_SUCCESS);
} else {
mAudioFile = null;
mHandler.sendEmptyMessage(Constant.RECORD_TOO_SHORT);
}
//录音完成释放资源
releaseRecorder();
}
/**
* @description 翻放录音相关资源
* @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();
//页面销毁,线程要关闭
mExecutorService.shutdownNow();
}
/*******6.0以上版本手机权限处理***************************/
/**
* @description 兼容手机6.0权限管理
* @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 开始播放音频文件
* @author ldm
* @time 2017/2/9 16:56
*/
private void startPlay(File mFile) {
try {
//初始化播放器
mediaPlayer = new MediaPlayer();
//设置播放音频数据文件
mediaPlayer.setDataSource(mFile.getAbsolutePath());
//设置播放监听事件
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
//播放完成
playEndOrFail(true);
}
});
//播放发生错误监听事件
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
playEndOrFail(false);
return true;
}
});
//播放器音量配置
mediaPlayer.setVolume(1, 1);
//是否循环播放
mediaPlayer.setLooping(false);
//准备及播放
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;
}
}
}
页面布局
<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="开始录音"
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>
对应资源文件strings.xml:
<resources>
<string name="app_name">mediarecorder</string>
<string name="record_fail">录音失败</string>
<string name="time_too_short">时间太短,请重新录音</string>
<string name="play_over">播放完成</string>
<string name="play_error">抱歉,播放发生异常</string>
<string name="stop_by_up">松开停止录音</string>
<string name="speak_by_press">按住说话</string>
<string name="start_record">开始录音</string>
<string name="stop_record">停止录音</string>
</resources>
录音相关权限 :
<!--SD卡权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--录音权限-->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
安卓录制播放音频:https://github.com/ldm520/Android_Media


猜你喜欢
- @GetMapping注解携带参数方式今天突然发现,当我们根据id查询用户信息时,如果不想通过localhost:8080//findOne
- 前言此前部门内的一个线上系统上线后内存一路飙高、一段时间后直接占满。协助开发人员去分析定位,发现内存中某个Object的量远远超出了预期的范
- 概述在实际项目开发中如果需要支持多语言,我们需要整理项目中所有的字符串并翻译成对应的语种放在相应的文件夹下,就像这样最让我们头痛的是我们得一
- 如下所示:package test.com.gjob.services; import java.util.Properties
- 简介Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同
- 1.问题项目中有自己企业的通讯录,但是在应用中拨打公司通讯录的联系人,由于手机通讯录中没有相应的信息,只显示一串电话号2 .目的监听系统来电
- 本文实例为大家分享了C#实现简单点餐系统的具体代码,供大家参考,具体内容如下实现简单点餐系统剖析1、设计界面(给菜品的序号(index),名
- WinForm RichTextBox文本动态滚动显示文本方在RichTextBox动态显示一些文本信息时,需要一些设置,显示当前要显示的字
- 一直写过数组全排列的算法,当时接触的是使用回溯的方法,这样可以保证生成的全排列一定是按照字典序的,但是今天在做leetcode上的一道题时,
- 我们知道,在 Python 里面,如果你要运行一段 JavaScript,你可以使用execJS这种第三方库。那么在 Golang 里面,你
- 我们从书本上学到什么?最明显的,也是直观的方式,在Java中生成随机数只要简单的调用:java.lang.Math.random()在所有其
- 在android平板上用webview打开一个网页,调用里面的javascript方法,同时相互传参。网上例子很少啊,基本都不能获取返回值,
- 编程是一门艺术,大批量的改动显然是非常丑陋的做法,用心的琢磨写的代码让它变的更美观。在软件开发系统中,**“方法的请求者
- java引用传递的三种类型我这里使用了mldn视频里的例子,只用于学习交流。第一种结果:调用前:50调用后:1000分析:理解:好理解第二种
- 前言:干了这几个项目,也做过几次文件上传下载,要么是copy项目以前的代码,要么是百度的,虽然做出来了,但学习一下原理弄透彻还是很有必要的。
- 前言在JDK当中给我们提供的各种并发工具当中,比如ReentrantLock等等工具的内部实现,经常会使用到一个工具,这个工具就是LockS
- 四大函数式接口新时代的程序员:lambda 表达式,链式编程,函数式接口,Stream 流式计算函数式接口: 只有一个方法的接口@Funct
- mybatis-plus想要修改某字段为null问题场景使用mybatis + mybatisPlus进行修改某字段,想要将其设为null,
- 前言C# 的编译器可以对代码进行优化,所以,我们在写代码的时候,可以更多地考虑一下代码的易读性问题。不考虑基本的对齐和换行美化。看一下局部变
- 一、前言在java中,异常机制是非常有用的构成部分,异常信息对于查找错误来说是必不可少至关重要的信息,因此我们希望在发生错误的时候先看到捕捉