### 前言
> 最近项目中需要用到录音的功能,搞定需求之后,花了些时间封装成一个录音的工具包,分享给大家,需要源码的 [点击这里](https://github.com/developerHaoz/SoundRecorderUtils)。
先贴个效果图给大家看一下,看看这个录音包的功能。
![SoundRecorderUtils.gif](http://upload-images.jianshu.io/upload_images/4334738-d1288c24e6707d59.gif?imageMogr2/auto-orient/strip)
### 一、实现录音的 Service
-----
这个类可以说是这个包的核心了,如果理解了这个 `Service`,录音这一块基本就没什么问题了。
录音主要是利用 `MediaRecoder` 这个类,进行声音的记录,接下来我们一起来看看具体的实现。
```
public class RecordingService extends Service {
private String mFileName;
private String mFilePath;
private MediaRecorder mRecorder;
private long mStartingTimeMillis;
private long mElapsedMillis;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startRecording();
return START_STICKY;
}
@Override
public void onDestroy() {
if (mRecorder != null) {
stopRecording();
}
super.onDestroy();
}
// 开始录音
public void startRecording() {
setFileNameAndPath();
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");
}
}
// 设置录音文件的名字和保存路径
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,对于 DialogFragment 不是很了解,可以先看看我这篇文章 [Android 撸起袖子,自己封装 DialogFragment](http://www.jianshu.com/p/c9f20ec7277a)。
```
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_GRANT