Android实现录音功能

1 Android录音需要声明录音权限

<uses-permission android:name="android.permission.RECORD_AUDIO" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

2.录音文件要写到文件夹中,创建文件夹,在Application的onCreate方法中创建文件夹

@Override

public void onCreate() {

    super.onCreate();

    CrashHandler mCrashHandler = CrashHandler.getInstance();

    mCrashHandler.init(getApplicationContext(), getClass());

    initFile();

}

private void initFile() {

    //录音文件

    File audioFile = new File(Constant.UrlAudio);

    if (!audioFile.exists()) {

        audioFile.mkdirs();

    } else if (!audioFile.isDirectory()) {

        audioFile.delete();

        audioFile.mkdirs();

    }

    //拍摄图片文件

    File imageFile = new File(Constant.UrlImage);

    if (!imageFile.exists()) {

        imageFile.mkdirs();

    } else if (!imageFile.isDirectory()) {

        imageFile.delete();

        imageFile.mkdirs();

    }

}

Constant.UrlImage是个静态的文件路径

//录音文件

public static String UrlAudio = FileUtil.getSdcardPathOnSys()+"/EhmFile/media/audio/";

3.在activity中开始录音

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import android.os.Environment;

import android.os.Handler;

import android.os.Message;

import android.media.MediaRecorder;

import android.text.format.DateFormat;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import java.io.File;

import java.io.IOException;

import java.util.Calendar;

import java.util.Locale;

public class Record2Activity extends AppCompatActivity {

    // 录音界面相关

    Button btnStart;

    Button btnStop;

    TextView textTime;

    // 录音功能相关

    MediaRecorder mMediaRecorder; // MediaRecorder 实例

    boolean isRecording; // 录音状态

    String fileName; // 录音文件的名称

    String filePath; // 录音文件存储路径

    Thread timeThread; // 记录录音时长的线程

    int timeCount; // 录音时长 计数

    final int TIME_COUNT = 0x101;

    // 录音文件存放目录

    final String audioSaveDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/audiodemo/";

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_record2);

        btnStart = (Button) findViewById(R.id.btn_start);

        btnStop = (Button) findViewById(R.id.btn_stop);

        textTime = (TextView) findViewById(R.id.text_time);

        btnStart.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

// 开始录音

                btnStart.setEnabled(false);

                btnStop.setEnabled(true);

                startRecord();

                isRecording = true;

// 初始化录音时长记录

                timeThread = new Thread(new Runnable() {

                    @Override

                    public void run() {

                        countTime();

                    }

                });

                timeThread.start();

            }

        });

        btnStop.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

// 停止录音

                btnStart.setEnabled(true);

                btnStop.setEnabled(false);

                stopRecord();

                isRecording = false;

            }

        });

    }

    // 记录录音时长

    private void countTime() {

        while (isRecording) {

            Log.d("mediaRe","正在录音");

            timeCount++;

            Message msg = Message.obtain();

            msg.what = TIME_COUNT;

            msg.obj = timeCount;

            myHandler.sendMessage(msg);

            try {

                timeThread.sleep(1000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

        Log.d("mediaRec", "结束录音");

        timeCount = 0;

        Message msg = Message.obtain();

        msg.what = TIME_COUNT;

        msg.obj = timeCount;

        myHandler.sendMessage(msg);

    }

    /**

    * 开始录音 使用amr格式

    * 录音文件

    *

    * @return

    */

    public void startRecord() {

// 开始录音

        /* ①Initial:实例化MediaRecorder对象 */

        if (mMediaRecorder == null)

            mMediaRecorder = new MediaRecorder();

        try {

            /* ②setAudioSource/setVedioSource */

            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风

            /*

            * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式

            * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB)

            */

            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);

            /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */

            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

            fileName = DateFormat.format("yyyyMMdd_HHmmss", Calendar.getInstance(Locale.CHINA)) + ".m4a";

            //注意文件夹要创建之后才能使用

            filePath = Constant.UrlAudio + fileName;

            /* ③准备 */

            mMediaRecorder.setOutputFile(filePath);

            mMediaRecorder.prepare();

            /* ④开始 */

            mMediaRecorder.start();

        } catch (IllegalStateException e) {

            Log.i("mediaEr", "call startAmr(File mRecAudioFile) failed!" + e.getMessage());

        } catch (IOException e) {

            e.printStackTrace();

            Log.i("mediaEr", "call startAmr(File mRecAudioFile) failed!" + e.getMessage());

        }

    }

    /**

    * 停止录音

    */

    public void stopRecord() {

//有一些网友反应在5.0以上在调用stop的时候会报错,翻阅了一下谷歌文档发现上面确实写的有可能会报错的情况,捕获异常清理一下就行了,感谢大家反馈!

        try {

            mMediaRecorder.stop();

            mMediaRecorder.release();

            mMediaRecorder = null;

            filePath = "";

        } catch (RuntimeException e) {

            Log.e("mediaR", e.toString());

            mMediaRecorder.reset();

            mMediaRecorder.release();

            mMediaRecorder = null;

            File file = new File(filePath);

            if (file.exists())

                file.delete();

            filePath = "";

        }

    }

    // 格式化 录音时长为 秒

    public static String FormatMiss(int miss) {

        return "" + miss;

    }

    Handler myHandler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            switch (msg.what) {

                case TIME_COUNT:

                    int count = (int) msg.obj;

                    Log.d("meidaRe","count == " + count);

                    textTime.setText(FormatMiss(count));

                    break;

            }

        }

    };

    @Override

    protected void onDestroy() {

        super.onDestroy();

        myHandler.removeCallbacksAndMessages(null);

    }

}

布局文件很简单

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:app="http://schemas.android.com/apk/res-auto"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context=".Record2Activity">

    <Button

        android:id="@+id/btn_stop"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="结束"

        app:layout_constraintBottom_toBottomOf="parent"

        app:layout_constraintEnd_toEndOf="parent"

        app:layout_constraintHorizontal_bias="0.5"

        app:layout_constraintStart_toEndOf="@+id/btn_start"

        app:layout_constraintTop_toTopOf="parent" />

    <Button

        android:id="@+id/btn_start"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="开始"

        app:layout_constraintBottom_toBottomOf="parent"

        app:layout_constraintEnd_toStartOf="@+id/btn_stop"

        app:layout_constraintHorizontal_bias="0.5"

        app:layout_constraintStart_toStartOf="parent"

        app:layout_constraintTop_toTopOf="parent" />

    <TextView

        android:id="@+id/text_time"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_marginStart="11dp"

        android:layout_marginTop="47dp"

        android:text="时间"

        app:layout_constraintStart_toStartOf="@+id/btn_start"

        app:layout_constraintTop_toBottomOf="@+id/btn_start" />

</androidx.constraintlayout.widget.ConstraintLayout>

这样就可以使用录音功能了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容