采用 MediaRecorder 实现录音功能

快速录音需求,还要求音频文件很小,呵呵,那就干吧:

package com.zzq.mydemo.test;

import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;

import com.zzq.mydemo.BaseActivity;
import com.zzq.mydemo.R;

import java.io.File;
import java.io.IOException;

/**
 * 录音 Activity
 * 

* */ public class RecordingActivity extends BaseActivity { private Dialog mDialog; private TextView mTvCountdown; private TimeCount mTimeCount; private MediaRecorder mRecorder; private String mFileName; private String mFilePath; public static final String FILE_NAME = "FILE_NAME"; public static final String FILE_PATH = "FILE_PATH"; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); showDialog(); initMediaRecorder(); // 多加 100毫秒,用于补偿 TimeCount的时间损耗,解决跳 1秒的 BUG【目前很好用】 mTimeCount = new TimeCount(10100, 1000); } private void initMediaRecorder() { mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS); mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mRecorder.setAudioChannels(1); // 设置录音文件的清晰度,这里将 1s音频压缩到 1k大小 mRecorder.setAudioSamplingRate(8000); mRecorder.setAudioEncodingBitRate(8000); } @Override protected void onDestroy() { stopRecording(); if (mTimeCount != null) { mTimeCount.cancel(); } if (mDialog != null && mDialog.isShowing()) { mDialog.dismiss(); mDialog = null; } super.onDestroy(); } private void showDialog() { mDialog = new Dialog(this, R.style.NormalDialogStyle); View view = View.inflate(this, R.layout.dialog_recording, null); WindowManager.LayoutParams lp = mDialog.getWindow().getAttributes(); lp.width = WindowManager.LayoutParams.WRAP_CONTENT; lp.height = WindowManager.LayoutParams.WRAP_CONTENT; lp.gravity = Gravity.CENTER; mDialog.setContentView(view, lp); // 设置点击对话框外部是否关闭对话框 mDialog.setCanceledOnTouchOutside(true); // 点击对话框外部或者点击返回键都会触发此回调 mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { forResult(); } }); mTvCountdown = (TextView) view.findViewById(R.id.tv_countdown); Button btnRecording = (Button) view.findViewById(R.id.btn_recording); btnRecording.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startRecording(); break; case MotionEvent.ACTION_UP: forResult(); break; default: break; } return true; } }); mDialog.show(); } private class TimeCount extends CountDownTimer { private TimeCount(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval); } @Override public void onTick(long millisUntilFinished) { mTvCountdown.setText(millisUntilFinished / 1000 + "s"); } @Override public void onFinish() { forResult(); } } private void startRecording() { mTimeCount.start(); setFileNameAndPath(); // 设置录音文件的保存路径 mRecorder.setOutputFile(mFilePath); try { mRecorder.prepare(); mRecorder.start(); } catch (IOException e) { Log.e("RecordingActivity", "prepare() failed"); } } /** * 设置录音文件的名字和保存路径 */ private void setFileNameAndPath() { mFileName = System.currentTimeMillis() + ".aac"; mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/SoundRecorder/" + mFileName; File file = new File(mFilePath); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } } private void stopRecording() { if (mRecorder != null) { try { mRecorder.stop(); } catch (IllegalStateException e) { // 如果当前java状态和jni里面的状态不一致 Log.e("RecordingActivity", "stop() failed"); } mRecorder.release(); mRecorder = null; } } private void forResult() { Intent intent = new Intent(); intent.putExtra(FILE_NAME, mFileName); intent.putExtra(FILE_PATH, mFilePath); setResult(RESULT_OK, intent); finish(); } }

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#ffffff"
    android:orientation="vertical"
    android:paddingLeft="30dp"
    android:paddingRight="30dp">

    <TextView
        android:id="@+id/tv_countdown"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:gravity="center"
        android:text="10s"
        android:textColor="#ff0000"
        android:textSize="30sp" />

    <Button
        android:id="@+id/btn_recording"
        android:layout_width="250dp"
        android:layout_height="50dp"
        android:layout_gravity="center"
        android:layout_marginBottom="30dp"
        android:layout_marginTop="30dp"
        android:text="按住开始录音"
        android:textColor="#ffffff"
        android:textSize="18sp" />

</LinearLayout>

其中有趣的点:
1、录音前先初始化 MediaRecorder:initMediaRecorder(),省时间,做到点击录音后就可以马上去录音;
2、通过调整 setAudioSamplingRate(int i) 和 setAudioEncodingBitRate(int i) 可以控制所录音频的质量,从而调整音频文件大小;
3、退出时要及时清理垃圾;
4、TimeCount有个跳 1秒的 BUG,这是程序运行耗时不断累积造成的,通过多加 100毫秒的方式,来补偿 TimeCount 的时间损耗,解决跳 1秒的 BUG,目前很好用(不知道有没有坑 >_<|||):

mTimeCount = new TimeCount(10100, 1000);

5、设置 setCanceledOnTouchOutside(true) 时,要监听此行为并结束录音:

        // 设置点击对话框外部是否关闭对话框
        mDialog.setCanceledOnTouchOutside(true);
        // 点击对话框外部或者点击返回键都会触发此回调
        mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                forResult();
            }
        });

6、用了 “按住录音,放开停止” 的方式:

        Button btnRecording = (Button) view.findViewById(R.id.btn_recording);
        btnRecording.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        startRecording();
                        break;
                    case MotionEvent.ACTION_UP:
                        forResult();
                        break;
                    default:
                        break;
                }
                return true;
            }
        });

下面是调用录音页面及播放所录音频的代码:

public class MainActivity extends BaseActivity {

    private MediaPlayer mMediaPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    public void onJump(View view) {
        Intent intent = new Intent(this, RecordingActivity.class);
        startActivityForResult(intent, 1);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            String fileName = data.getStringExtra(RecordingActivity.FILE_NAME);
            String filePath = data.getStringExtra(RecordingActivity.FILE_PATH);
            if (!filePath.isEmpty()) {
                if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                    stopPlaying();
                    return;
                }
                playAudio(false, filePath);
            }
        }
    }

    private void playAudio(boolean isInternet, String filePath) {
        if (mMediaPlayer == null) {
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    stopPlaying();
                }
            });
        }
        try {
            if (isInternet) {
                mMediaPlayer.setDataSource(this, Uri.parse(filePath));
            } else {
                mMediaPlayer.setDataSource(filePath);
            }
            mMediaPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mMediaPlayer.start();
    }

    private void stopPlaying() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    @Override
    protected void onDestroy() {
        stopPlaying();
        super.onDestroy();
    }
}

参考文章:
1、https://www.jianshu.com/p/6bbb51ac4938
2、https://www.jianshu.com/p/6d91a8d7b974
3、https://blog.csdn.net/hecheng2009/article/details/74807032
4、https://blog.csdn.net/feiyu1947/article/details/85332322

你可能感兴趣的:(Android,常用代码)