编程语言
首页 > 编程语言> > Android-录音功能直接拿去用,程序员必学之一

Android-录音功能直接拿去用,程序员必学之一

作者:互联网

 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。

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();
### 尾声

评论里面有些同学有疑问关于如何学习material design控件,我的建议是**去GitHub搜**,有很多同行给的例子,这些栗子足够入门。

有朋友说要是动真格的话,需要NDK以及JVM等的知识,首现**NDK并不是神秘的东西,**你跟着官方的步骤走一遍就知道什么回事了,**无非就是一些代码格式以及原生/JAVA内存交互,进阶一点的有原生/JAVA线程交互,线程交互确实有点蛋疼,但平常避免用就好了,再说对于初学者来说关心NDK干嘛,据鄙人以前的经历,只在音视频通信和一个嵌入式信号处理(离线)的两个项目中用过,嵌入式信号处理是JAVA->NDK->.SO->MATLAB这样调用的我原来MATLAB的代码,其他的大多就用在游戏上了吧,一般的互联网公司会有人给你公司的SO包的。**
**至于JVM,该掌握的那部分,相信我,你会掌握的,不该你掌握的,有那些专门研究JVM的人来做,不如省省心有空看看计算机系统,编译原理。**

**一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。**
**初学者,一句话,多练。**

> **[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

标签:mStartRecording,必学,录音,public,getActivity,程序员,new,Android,mRecorder
来源: https://blog.csdn.net/m0_61072747/article/details/120131801