[Android多媒体二]调用系统录音机录音并存储到指定位置,适配安卓 7.0

标签: Android 调用系统录音机 将录音保存到指定位置
20人阅读 评论(0) 收藏 举报
分类:

接上一篇文章,[Android多媒体一]调用系统相机拍照并存储到指定位置,适配安卓 7.0

本文讲述如何调用系统录音机,完成录音后,对录音进行指定位置的保存。

一、开始编写

首先,还是理清一下思路,在着手编写代码。

1、启动系统录音机并保存到指定位置依然设计读写权限,此时需要向用户请求权限,并根据用户操作进行相应的动作。录音使用到的权限有:

    <!-- 读写权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 录音权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

2、将录音文件保存到SD的指定位置,需要创建一定的目录层级,像上一篇文章讲述的一样,这次,把录音文件保存为SD根目录下的TestDir/voice/xxx.amr。

3、根据用户录音的结果,进行存储操作。

4、不同于启动相机拍照,这次,不把uri加入到启动录音机的额外数据,因为不管加还是不加,录音成功后,获取到的uri都是系统存放刚刚的录音文件的uri。

废话不多说,直接上代码:

主界面布局文件:

<?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="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拍照并保存" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="录音并保存" />

</LinearLayout>

MainActivity代码:

package com.my.example.multimediatest;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.StrictMode;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    public static final String SD_APP_DIR_NAME = "TestDir"; //存储程序在外部SD卡上的根目录的名字
    public static final String PHOTO_DIR_NAME = "photo";    //存储照片在根目录下的文件夹名字
    public static final String VOICE_DIR_NAME = "voice";    //存储音频在根目录下的文件夹名字
    public static final String VIDEO_DIR_NAME = "video";    //存储视频在根目录下的文件夹名字

    public static final int PHOTO_RESULT_CODE = 100;        //标志符,图片的结果码,判断是哪一个Intent
    public static final int VOICE_RESULT_CODE = 101;        //标志符,音频的结果码,判断是哪一个Intent
    public static final int VIDEO_RESULT_CODE = 102;        //标志符,视频的结果码,判断是哪一个Intent

    private String mImagePath;             //用于存储图片的最终目录,即根目录 / 图片的文件夹 / 图片
    private Uri mImageUri;                 //存储相机返回的uri
    private String mImageName;             //保存的图片的名字
    private File mImageFile;               //图片文件

    private String mVoicePath;             //用于存储录音的最终目录,即根目录 / 录音的文件夹 / 录音
    private String mVoiceName;             //保存的录音的名字
    private File mVoiceFile;               //录音文件


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d(TAG, "开始...");

        // android 7.0系统解决拍照的问题
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
        builder.detectFileUriExposure();

        //拍照按钮的点击事件
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]
                        {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 200);
            }
        });

        //录音按钮的点击事件
        Button button1 = (Button) findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]
                        {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 201);
            }
        });
    }

    /**
     * 返回用户是否允许权限的结果,并处理
     */
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResult) {
        if (requestCode == 200) {
            //用户允许权限
            if (grantResult[0] == PackageManager.PERMISSION_GRANTED && grantResult[1] == PackageManager.PERMISSION_GRANTED) {
                Log.d(TAG, "用户已允许权限,准备启动相机。");
                //启动照相机
                startCamera();
            } else {  //用户拒绝
                Log.d(TAG, "用户已拒绝权限,程序终止。");
                Toast.makeText(this, "程序需要足够权限才能运行", Toast.LENGTH_SHORT).show();
            }
        }
        if (requestCode == 201) {
            //用户允许权限
            if (grantResult[0] == PackageManager.PERMISSION_GRANTED && grantResult[1] == PackageManager.PERMISSION_GRANTED) {
                //启动录音机
                startRecord();
            } else {
                Log.d(TAG, "用户已拒绝权限,程序终止。");
                Toast.makeText(this, "程序需要足够权限才能运行", Toast.LENGTH_SHORT).show();
            }
        }
    }

    /**
     * 启动录音机,创建文件
     */
    private void startRecord() {

        Intent intent = new Intent();
        intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
        createVoiceFile();
        Log.d(TAG, "创建录音文件");
        //添加权限
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Log.d(TAG, "启动系统录音机,开始录音...");
        startActivityForResult(intent, VOICE_RESULT_CODE);
    }

    /**
     * 创建音频目录
     */
    private void createVoiceFile() {
        mVoiceName = getMyTime() + ".amr";
        Log.d(TAG, "录音文件名称:" + mVoiceName);
        mVoiceFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                + "/" + SD_APP_DIR_NAME + "/" + VOICE_DIR_NAME + "/", mVoiceName);
        mVoicePath = mVoiceFile.getAbsolutePath();
        mVoiceFile.getParentFile().mkdirs();
        Log.d(TAG, "按设置的目录层级创建音频文件,路径:" + mVoicePath);
        mVoiceFile.setWritable(true);
    }

    /**
     * 启动相机,创建文件,并要求返回uri
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void startCamera() {
        Intent intent = new Intent();
        //指定动作,启动相机
        intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        Log.d(TAG, "指定启动相机动作,完成。");
        //创建文件
        createImageFile();
        Log.d(TAG, "创建图片文件结束。");
        //添加权限
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Log.d(TAG, "添加权限。");
        //获取uri
        mImageUri = FileProvider.getUriForFile(this, "com.my.example.multimediatest.provider", mImageFile);
        Log.d(TAG, "根据图片文件路径获取uri。");
        //将uri加入到额外数据
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
        Log.d(TAG, "将uri加入启动相机的额外数据。");
        Log.d(TAG, "启动相机...");
        //启动相机并要求返回结果
        startActivityForResult(intent, PHOTO_RESULT_CODE);
        Log.d(TAG, "拍摄中...");
    }

    /**
     * 创建图片文件
     */
    private void createImageFile() {
        Log.d(TAG, "开始创建图片文件...");
        //设置图片文件名(含后缀),以当前时间的毫秒值为名称
        mImageName = getMyTime() + ".jpg";
        Log.d(TAG, "设置图片文件的名称为:" + mImageName);
        //创建图片文件
        mImageFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                + "/" + SD_APP_DIR_NAME + "/" + PHOTO_DIR_NAME + "/", mImageName);
        //将图片的绝对路径设置给mImagePath,后面会用到
        mImagePath = mImageFile.getAbsolutePath();
        //按设置好的目录层级创建
        mImageFile.getParentFile().mkdirs();
        Log.d(TAG, "按设置的目录层级创建图片文件,路径:" + mImagePath);
        //不加这句会报Read-only警告。且无法写入SD
        mImageFile.setWritable(true);
        Log.d(TAG, "将图片文件设置可写。");
    }

    /**
     * 处理返回结果。
     * 1、图片
     * 2、音频
     * 3、视频
     *
     * @param requestCode 请求码
     * @param resultCode  结果码 成功 -1 失败 0
     * @param data        返回的数据
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        super.onActivityResult(requestCode, resultCode, data);

//        Log.d(TAG, "拍摄结束。");
        Log.d(TAG, "录音结束。");
        if (resultCode == Activity.RESULT_OK) {
            Log.d(TAG, "返回成功。");
            Log.d(TAG, "请求码:" + requestCode + "  结果码:" + resultCode + "  data:" + data);
            switch (requestCode) {
                case PHOTO_RESULT_CODE: {
                    Bitmap bitmap = null;
                    try {
                        //根据uri设置bitmap
                        bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), mImageUri);
                        Log.d(TAG, "根据uri设置bitmap。");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    //将图片保存到SD的指定位置
                    savePhotoToSD(bitmap);
                    //更新系统图库
                    updateSystemGallery();
                    Log.d(TAG, "结束。");
                    break;
                }
                case VOICE_RESULT_CODE: {
                    try {
                        Uri uri = data.getData();
                        String filePath = getAudioFilePathFromUri(uri);
                        Log.d(TAG, "根据uri获取文件路径:" + filePath);
                        Log.d(TAG, "开始保存录音文件");
                        saveVoiceToSD(filePath);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }

                    break;
                }
                case VIDEO_RESULT_CODE: {
//                    saveVideoTOSD();
                    break;
                }
            }
        }
    }

    /**
     * 保存照片到SD卡的指定位置
     */
    private void savePhotoToSD(Bitmap bitmap) {
        Log.d(TAG, "将图片保存到指定位置。");
        //创建输出流缓冲区
        BufferedOutputStream os = null;
        try {
            //设置输出流
            os = new BufferedOutputStream(new FileOutputStream(mImageFile));
            Log.d(TAG, "设置输出流。");
            //压缩图片,100表示不压缩
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
            Log.d(TAG, "保存照片完成。");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    //不管是否出现异常,都要关闭流
                    os.flush();
                    os.close();
                    Log.d(TAG, "刷新、关闭流");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 更新系统图库
     */
    private void updateSystemGallery() {
        //把文件插入到系统图库
        try {
            MediaStore.Images.Media.insertImage(this.getContentResolver(),
                    mImageFile.getAbsolutePath(), mImageName, null);
            Log.d(TAG, "将图片文件插入系统图库。");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 最后通知图库更新
        this.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + mImagePath)));
        Log.d(TAG, "通知系统图库更新。");
    }

    /**
     * 获取日期并格式化
     * 如:2017_10_20 周三 上午 11:20:35
     *
     * @return 格式化好的日期字符串
     */
    private String getMyTime() {
        //存储格式化后的时间
        String time;
        //存储上午下午
        String ampTime = "";
        //判断上午下午,am上午,值为 0 ; pm下午,值为 1
        int apm = Calendar.getInstance().get(Calendar.AM_PM);
        if (apm == 0) {
            ampTime = "上午";
        } else {
            ampTime = "下午";
        }
        //设置格式化格式
        SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd E " + ampTime + " kk:mm:ss");
        time = format.format(new Date());

        return time;
    }

    /**
     * 保存音频到SD卡的指定位置
     *
     * @param path 录音文件的路径
     */
    private void saveVoiceToSD(String path) {
        //创建输入输出
        InputStream isFrom = null;
        OutputStream osTo = null;
        try {
            //设置输入输出流
            isFrom = new FileInputStream(path);
            osTo = new FileOutputStream(mVoicePath);
            byte bt[] = new byte[1024];
            int len;
            while ((len = isFrom.read(bt)) != -1) {
                Log.d(TAG, "len = " + len);
                osTo.write(bt, 0, len);
            }
            Log.d(TAG, "保存录音完成。");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (osTo != null) {
                try {
                    //不管是否出现异常,都要关闭流
                    osTo.close();
                    Log.d(TAG, "关闭输出流");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (isFrom != null) {
                try {
                    isFrom.close();
                    Log.d(TAG, "关闭输入流");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 保存视频到SD卡的指定位置
     */
    private void saveVideoTOSD() {

    }

    /**
     * 通过Uri,获取录音文件的路径(绝对路径)
     *
     * @param uri 录音文件的uri
     * @return 录音文件的路径(String)
     */
    private String getAudioFilePathFromUri(Uri uri) {
        Cursor cursor = getContentResolver()
                .query(uri, null, null, null, null);
        cursor.moveToFirst();
        int index = cursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);
        String temp = cursor.getString(index);
        cursor.close();
        return temp;
    }

}

实现思路和拍摄图片并保存的思路一致,都是在启动系统程序前检查有无读写权限,有权限则创建对应的目录层级和对应的文件,操作完成后通过返回的结果进行保存操作。

二、运行结果

还是完整的执行流程。

接下来去系统的文件夹下找到刚刚录音的文件。


可以看到保存的位置、文件名称都符合预期。并且可以播放,文件大小由于是amr格式,所以非常小。

实现启动录音机录音,并保存到指定位置还是非常简单的,代码量相对于相机来说少了很多。逻辑也更清晰了。


相关文章:[Android多媒体一]调用系统相机拍照并存储到指定位置,适配安卓 7.0


原创文章,转载请注明出处:https://blog.csdn.net/Lone1yCode/article/details/79951477


查看评论

细细品味ASP.NET (二)

细细品味ASP.NET (二)青苹果工作室(编译) 01-5-17 下午 01:33:55Web表单 ASP.NET Web Forms就是Web页面,同现在你用 ASP编写代码所做的工作是一样的。...
  • tchaikov
  • tchaikov
  • 2001-05-17 18:50:00
  • 996

android 调用系统自带录音机

  • 2013年10月10日 13:53
  • 968KB
  • 下载

android 两种调用系统资源实现录音

 在移动APP开发中,每逢APP应用设计到多媒体开发的时候,都会让很多的程序员头疼不已,而且项目的开发进度会放慢、项目 的难度也会加大蛮多,同时APP的测试也会增加。Android中的多媒体开...
  • qq_26336495
  • qq_26336495
  • 2015-05-05 16:33:23
  • 2442

android进行录音功能并保存播放

在android中进行录音相对来说是比较简单的,使用系统提供的MediaRecorder类进行录音并保存,然后调用MediaPlayer进行播放。...
  • MyName_kk
  • MyName_kk
  • 2015-11-18 17:17:17
  • 8662

Android调用手机系统自带录音功能实现语音录制与播放

首先调用手机系统自带的录音功能需要相关权限以及读写SD卡的权限 uses-permission android:name="android.permission.RECORD_AUDIO">uses...
  • csj731742019
  • csj731742019
  • 2016-12-20 17:20:01
  • 1204

Android6.0源码分析之录音功能(一)

从现在开始一周时间研究录音,下周出来一个完整的博客,监督,激励!!! 2017-02-09--------2017-02-17...
  • zrf1335348191
  • zrf1335348191
  • 2017-02-09 17:31:58
  • 8746

Android 录音和摄像头权限适配

最近在研究权限适配的相关内容,整理以前的权限博客如下:   android permission权限与安全机制解析(上)   android permission权限与安全机制解析(下)   A...
  • zhao_zepeng
  • zhao_zepeng
  • 2016-10-29 19:33:33
  • 6470

Android实现录音功能及播放语音功能

Android中实现录音功能其实很简单,直接调用的系统的就ok了,这里就不写实现的原理了,直接部署代码:所谓的实现就是用的MediaRecorder。 录音功能代码:  //开始录制     p...
  • ming_147
  • ming_147
  • 2016-08-27 16:01:08
  • 2003

Android开发--WebView简单录音功能的实现

WebView实现本地录音
  • bird3014
  • bird3014
  • 2017-06-12 19:03:46
  • 1239

Android原生录音机

  • 2015年09月07日 15:19
  • 1.93MB
  • 下载
    个人资料
    持之以恒
    等级:
    访问量: 7612
    积分: 287
    排名: 27万+
    文章存档