Android使用MediaRecorder实现录音及播放

作者:迟做总比不做强 时间:2021-09-10 00:46:41 

现在项目中有使用到音视频相关技术,在参考了网上各种大牛的资料及根据自己项目实际情况(兼容安卓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

标签:Android,MediaRecorder,录音
0
投稿

猜你喜欢

  • Java中死锁与活锁的具体实现

    2023-10-29 01:48:02
  • Java全面细致讲解Wrapper的使用

    2023-02-14 00:49:24
  • Android实现布局全屏

    2023-11-29 06:44:13
  • 探讨Java验证码制作(下篇)

    2023-04-04 11:21:42
  • Android实现关机重启的方法分享

    2022-11-13 21:32:33
  • Java 散列存储详解及简单示例

    2022-06-30 23:19:52
  • springboot配置aop切面日志打印过程解析

    2022-07-10 13:14:09
  • SpringBoot框架中Mybatis-plus的简单使用操作汇总

    2022-12-17 19:10:53
  • C#中关于zip压缩解压帮助类的封装 附源码下载

    2021-08-19 00:37:22
  • 通过FancyView提供 Android 酷炫的开屏动画实例代码

    2023-04-11 11:00:53
  • C# DataSet的内容写成XML时如何格式化字段数据

    2023-04-14 09:01:28
  • 详解kafka中的消息分区分配算法

    2021-06-02 08:16:15
  • Java开发环境jdk 1.8安装配置方法(Win7 64位系统/windows server 2008)

    2022-05-11 20:00:58
  • JAVA中常见异常类

    2021-11-09 09:47:20
  • java设计模式理解依赖于抽象不依赖具体的分析

    2023-09-13 01:41:45
  • C#实现把科学计数法(E)转化为正常数字值

    2022-09-06 23:41:47
  • 关于synchronized有趣的同步问题

    2021-11-03 05:46:55
  • 深入讲解我们说的CAS自旋锁到底是什么

    2022-05-21 14:43:08
  • Java JDK动态代理实现原理实例解析

    2022-04-23 05:19:10
  • Java定时器Timer使用方法详解

    2023-08-25 17:32:20
  • asp之家 软件编程 m.aspxhome.com