mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //录音文件保存的格式,这里保存为 mp4
 mRecorder.setOutputFile(mFilePath); // 设置录音文件的保存路径
 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
 mRecorder.setAudioChannels(1);
 // 设置录音文件的清晰度
 mRecorder.setAudioSamplingRate(44100);
 mRecorder.setAudioEncodingBitRate(192000);try {
 mRecorder.prepare();
 mRecorder.start();
 mStartingTimeMillis = System.currentTimeMillis();
 } catch (IOException e) {
 Log.e(LOG_TAG, “prepare() failed”);
 }
 }// 设置录音文件的名字和保存路径
 public void setFileNameAndPath() {
 File f;do {
 count++;
 mFileName = getString(R.string.default_file_name)• “_” + (System.currentTimeMillis()) + “.mp4”;
 mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath();
 mFilePath += “/SoundRecorder/” + mFileName;
 f = new File(mFilePath);
 } while (f.exists() && !f.isDirectory());
 }// 停止录音
 public void stopRecording() {
 mRecorder.stop();
 mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis);
 mRecorder.release();getSharedPreferences(“sp_name_audio”, MODE_PRIVATE)
 .edit()
 .putString(“audio_path”, mFilePath)
 .putLong(“elpased”, mElapsedMillis)
 .apply();
 if (mIncrementTimerTask != null) {
 mIncrementTimerTask.cancel();
 mIncrementTimerTask = null;
 }mRecorder = null;
 }}
可以看到在 onStartCommand() 里面有一个 startRecording() 方法,在外部启动这个RecordingService 的时候,便会调用这个 startRecording() 方法开始录音。
在 startRecording() 方法中先调用了 setFileNameAndPath 方法,初始化了录音文件的名字和保存的路径,为了让每个录音文件都有唯一的名字,我调用System.currentMillis() 拼接到录音文件的名字里面。
public void setFileNameAndPath() {
 File f;do {
 count++;
 mFileName = getString(R.string.default_file_name)• “_” + (System.currentTimeMillis()) + “.mp4”;
 mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath();
 mFilePath += “/SoundRecorder/” + mFileName;
 f = new File(mFilePath);
 } while (f.exists() && !f.isDirectory());
 }设置好了文件的名字和保存路径之后,对 mRecorder 进行一系列参数的设置,这个mRecorder 是 MediaRecorder 的一个实例,专门用于录音的存储。
mRecorder = new MediaRecorder();
 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //录音文件保存的格式,这里保存为 mp4
 mRecorder.setOutputFile(mFilePath); // 设置录音文件的保存路径
 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
 mRecorder.setAudioChannels(1);
 // 设置录音文件的清晰度
 mRecorder.setAudioSamplingRate(44100);
 mRecorder.setAudioEncodingBitRate(192000);try {
 mRecorder.prepare();
 mRecorder.start();
 mStartingTimeMillis = System.currentTimeMillis();
 } catch (IOException e) {
 Log.e(LOG_TAG, “prepare() failed”);
 }设置好参数之后,启动 mRecorder 开始录音,可以看到启动 mRecorder 开始录音后,我还将当前的时间赋值给 mStartingTimeMills,这里主要是为了记录录音的时长,等到录音结束后再获取一次当前的时间,然后将两个时间进行相减,就能得到录音的具体时长了。
等到录音结束,停止服务后,便会回调 RecordingService 的 onDestroy() 方法,这时候便会调用 stopRecording() 方法,关闭 mRecorder,并用 SharedPreferences 保存录音文件的信息,最后将 mRecorder 置空,防止内存泄露
public void stopRecording() {
 mRecorder.stop();
 mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis);
 mRecorder.release();getSharedPreferences(“sp_name_audio”, MODE_PRIVATE)
 .edit()
 .putString(“audio_name”, mFileName)
 .putString(“audio_path”, mFilePath)
 .putLong(“elpased”, mElapsedMillis)
 .apply();
 if (mIncrementTimerTask != null) {
 mIncrementTimerTask.cancel();
 mIncrementTimerTask = null;
 }mRecorder = null;
 }###二、显示录音界面的 RecordAudioDialogFragment
用户进行的时候,总不能让 App 跳转到另外一个界面吧,这样用户体验并不是很好,比较好的方法是显示一个对话框,让用户进行操作,既然要用对话框,必然离不开 DialogFragment。
public class RecordAudioDialogFragment extends DialogFragment {
private boolean mStartRecording = true;
long timeWhenPaused = 0;
private FloatingActionButton mFabRecord;
 private Chronometer mChronometerTime;public static RecordAudioDialogFragment newInstance(int maxTime) {
 RecordAudioDialogFragment dialogFragment = new RecordAudioDialogFragment();
 Bundle bundle = new Bundle();
 bundle.putInt(“maxTime”, maxTime);
 dialogFragment.setArguments(bundle);
 return dialogFragment;
 }@NonNull
 @Override
 public Dialog onCreateDialog(Bundle savedInstanceState) {
 Dialog dialog = super.onCreateDialog(savedInstanceState);
 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
 View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_record_audio, null);mFabRecord.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
 != PackageManager.PERMISSION_GRANTED) {
 ActivityCompat.requestPermissions(getActivity()
 , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1);
 }else {
 onRecord(mStartRecording);
 mStartRecording = !mStartRecording;
 }
 }
 });builder.setView(view);
 return builder.create();
 }private void onRecord(boolean start) {
 Intent intent = new Intent(getActivity(), RecordingService.class);
 if (start) {
 File folder = new File(Environment.getExternalStorageDirectory() + “/SoundRecorder”);
 if (!folder.exists()) {
 folder.mkdir();
 }mChronometerTime.setBase(SystemClock.elapsedRealtime());
 mChronometerTime.start();
 getActivity().startService(intent);} else {
 mChronometerTime.stop();
 timeWhenPaused = 0;
 getActivity().stopService(intent);
 }
 }
 }可以看到在 RecordAudioDialogFragment 有一个 newInstance(int maxTime) 的静态方法供外部调用,如果想设置录音的最大时长,直接传参数进去就行了。
好的,敲黑板,重点来了,其实这个对话框的重点部分就是在 onCreateDialog()中,我们先加载了我们自定义的对话框的布局,当点击录音的按钮的时候,先进行相关权限的申请,这里有个巨坑,录音权限 android.permission.RECORD_AUDIO 在不久前还是普通权限的,不知道什么时候突然变成了危险权限,需要我们进行申请,Google 真是会玩。
public Dialog onCreateDialog(Bundle savedInstanceState) {
 Dialog dialog = super.onCreateDialog(savedInstanceState);
 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
 View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_record_audio, null);mFabRecord.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
 != PackageManager.PERMISSION_GRANTED) {
 ActivityCompat.requestPermissions(getActivity()
 , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1);
 }else {
 onRecord(mStartRecording);
 mStartRecording = !mStartRecording;
 }
 }
 });builder.setView(view);
 return builder.create();
 }申请好权限之后便会调用 onRecord() 这个方法,然后将 boolean mStartRecording 进行反转,这样就不用写难看的 if else 了,直接改变 mStartRecording 的值,然后在onRecord() 里面进行处理
接下来看下 onRecord 干了什么
private void onRecord(boolean start) {
 Intent intent = new Intent(getActivity(), RecordingService.class);
 if (mStartRecording) {
 File folder = new File(Environment.getExternalStorageDirectory() + “/SoundRecorder”);
 if (!folder.exists()) {
 folder.mkdir();
 }mChronometerTime.setBase(SystemClock.elapsedRealtime());
 mChronometerTime.start();
 getActivity().startService(intent);} else {
 mChronometerTime.stop();
 timeWhenPaused = 0;
 getActivity().stopService(intent);
 }
 }