zoukankan      html  css  js  c++  java
  • Android6.0源码分析之录音功能(一)【转】

    本文转载自:http://blog.csdn.net/zrf1335348191/article/details/54949549

    从现在开始一周时间研究录音,下周出来一个完整的博客,监督,激励!!!

    2017-02-09--------2017-02-17

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    Android源码录音功能说起来似乎也很简单,只不过就是一个录音的功能然后进行了一个保存的操作。为什么要研究这个呢?毕竟现

    在语音通话、直播亦或者是语音助手比较流行,其中其实最基础的还是对录音的一些处理,所以还是有必要研究一下的。

    说起来功能简单,但其实单单是录音功能其中又夹杂着一些别的东西,比如UI的实时更新,电话等各种状态的监控,音量大小的监

    控。录音的保存也涉及到往手机中写数据,以及以何种格式写数据,比如当前流行的直播,音频流的传输到底以何种格式,手机可以

    播放什么样的格式,这些都会涉及。但是手机的原生系统应用录音机不支持文件的播放。

    代码所在目录为androidpackagesappsSoundRecorder

    先从编译开始说起

    chapter one 录音机编译脚本文件-Android.mk

    [java] view plain copy
     
    1. LOCAL_PATH:= $(call my-dir)  
    2. include $(CLEAR_VARS)  
    3.   
    4. LOCAL_MODULE_TAGS := optional  
    5.   
    6. LOCAL_SRC_FILES := $(call all-subdir-java-files)  
    7.   
    8. LOCAL_PACKAGE_NAME := SoundRecorder  
    9.   
    10. LOCAL_PRIVILEGED_MODULE := true  
    11.   
    12. include $(BUILD_PACKAGE)  

    对于Android.mk文件的详细介绍可参考连接http://www.cnblogs.com/welhzh/p/4532142.html

    简单介绍一下

    LOCAL_MODULE_TAGS := optional表示模块在任何时候都参与编译

    LOCAL_PACKAGE_NAME :=SoundRecorder表示编译完成后生成的应用名为SoundRecorder

    LOCAL_PRIVILEGED_MODULD := true表示APP会安装在~/system/priv-app下拥有系统权限

    编译脚本文件可以告诉我们APP的名字和APP会安装在哪里,以及APP是否参与编译

    chapter two,模块的入口清单配置文件--Androidmanifest.xml

    配置文件会告诉我们应用的入口,应用所需的权限以及应用的各种组件,看一个应用的复杂程度其实看配置文件即可

    [java] view plain copy
     
    1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     package="com.android.soundrecorder">  
    3.   
    4.     <original-package android:name="com.android.soundrecorder" />  
    5.   
    6.     <uses-permission android:name="android.permission.RECORD_AUDIO" />  
    7.     <uses-permission android:name="android.permission.INTERNET" />  
    8.     <uses-permission android:name="android.permission.WAKE_LOCK" />  
    9.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
    10.     <application android:label="@string/app_name"  
    11.                  android:icon="@drawable/ic_launcher_soundrecorder"  
    12.                  android:usesCleartextTraffic="false">  
    13.         <activity android:name="SoundRecorder"  
    14.                 android:configChanges="orientation|screenSize|keyboardHidden"  
    15.                 android:screenOrientation="unspecified"  
    16.                 android:clearTaskOnLaunch="true"  
    17.                 android:theme="@style/Theme.SoundRecorder">  
    18.             <intent-filter>  
    19.                 <action android:name="android.intent.action.MAIN" />  
    20.             </intent-filter>  
    21.             <intent-filter>  
    22.                  <action android:name="android.provider.MediaStore.RECORD_SOUND" />  
    23.                  <category android:name="android.intent.category.DEFAULT" />  
    24.             </intent-filter>  
    25.         </activity>  
    26.     </application>  
    27. </manifest>  


    权限有:

    1>,android.permission.RECORD_AUDIO:allows an application to record audio允许程序录制音频

    2>,android.permission.INTERNET:allows an application to open network sockets允许程序打开网络套接字,即允许程序进行联网

    3>,android.permission.WAKE_LOCK: Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming:允许程序使用电源屏幕锁保持手机不进入休眠或者变暗,即在录音时保持屏幕常亮

    4>,android.permission.WRITE_EXTERNAL_STORAGE:允许往内存中写入数据

    组件有:

    该模块就注册了一个activity组件----SoundRecorder

    组件下有一个clearTaskOnLaunch属性,由字面意思大家也可以看出来那就是再到launch界面后再次进入APP会清除栈内的activity重新加载,但这也得看系统的一些处理,如果系统按home回到launch就是要销毁所有activity的话那这个字段也没有任何意义了

    组件的action为android.provider.MediaStore.RECORD_SOUND三方应用可通过调用该action来调起录音界面,亲测有效~~

    [java] view plain copy
     
    1.   

    chapter three src进入源码

    界面布局文件:

    main.xml

    对应用进行整体浏览后发现录音所涉及到的知识由以下几个

    1>,录音计时

    2>,录音音量UI设计

    3>,开始,暂停,继续录音

    4>,停止录音

    5>,播放录音

    6>,以某种格式保存录音,文件类型支持amr,3gpp,aac,wav

    7>,显示录音文件列表

    其实总的来说也就是录音然后以某种格式保存的功能,只是在录音的过程中需要根据不同情况对UI进行一个更新。

    接下来对单个功能进行分析之前首先是对各个view的id进行一个标记,方便以后对UI上做修改

    ID标记完了之后我们基本上涉及到的UI上的修改都可以进行了,既然UI上的更新都是由录音功能衍生出来的,那就先从录音功能说

    起,顺带着分析再不同的状态下UI的显示变化。

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    通过上述对录音界面的分析可以看出负责各种功能的button有recordButton、playButton、stopButton、acceptButton、

    discardButton,还有负责界面UI更新显示的其他textview。对于button的点击事件找到onClick方法。

    [java] view plain copy
     
    1. /* 
    2.    * Handle the buttons. 
    3.    */  
    4.   public void onClick(View button) {  
    5.       if (!button.isEnabled())  
    6.           return;  
    7.       if (Build.VERSION.SDK_INT >= 23) {  
    8.           String[] operationPermissionNames = getOperationPermissionName(button.getId());  
    9.           if (operationPermissionNames == null ||  
    10.               checkOperationPermission(operationPermissionNames, button.getId()))  
    11.               processClickEvent(button.getId());  
    12.       } else {  
    13.           processClickEvent(button.getId());  
    14.       }  
    15.   }  

    当进行button的点击事件进行处理时涉及到一个6.0权限的问题,首先要保证权限已经申请成功,然后在processClickEvent再对各种

    点击事件进行处理。

    <1>,recordButton录音,中止,继续

    对于录音按钮的点击处理逻辑如下:

    也就是说在录音按钮时如果想要开启录音首先要经过两次判断

    第一是判断是否是中止状态,也就是说录音中止此时点击会继续录音。

    第二是判断是否是正在进行录音,如果正在进行,则点击时会中止正在进行的录音

    经过以上两步的判断后才会进入录音的准备,但至于录音是否需能够开启成功还需要看后续的判断。

    那么如果当前录音处于中止状态的话该如何继续录音呢???

    其实继续录音就三件事,一是开启录音,二是对录音进行开始计时,第三就是设置下当前的录音状态为正在录音

    [java] view plain copy
     
    1. public void resumeRecording() {  
    2.         if (mRecorder == null) {  
    3.             return;  
    4.         }  
    5.         try {  
    6.             //mRecorder为MeidaRecorder对象,开始录音  
    7.             mRecorder.start();  
    8.         } catch (RuntimeException exception) {  
    9.             setError(INTERNAL_ERROR);  
    10.             Log.e(TAG, "Resume Failed");  
    11.         }  
    12.         //开始录音计时  
    13.         mSampleStart = System.currentTimeMillis();  
    14.         //设置当前的状态为正在录音  
    15.         setState(RECORDING_STATE);  
    16.     }  

    那如果当前录音处于正在录音的状态该如何中止录音呢?????

    对照上述继续录音的代码可以看到中止录音进行了以下操作:一是暂停当前的录音,二是对本次录音(包括多次暂停和继续)的总时

    长进行一个累加并记录入mSampleLength,三是设置录音的状态为暂停的状态

    [java] view plain copy
     
    1. public void pauseRecording() {  
    2.         if (mRecorder == null) {  
    3.             return;  
    4.         }  
    5.         try {  
    6.             //mRecorder为MediaRecorder对象,暂停录音  
    7.            mRecorder.pause();  
    8.         } catch (RuntimeException exception) {  
    9.             setError(INTERNAL_ERROR);  
    10.             Log.e(TAG, "Pause Failed");  
    11.         }  
    12.         //记录从开始录音到现在的总的录音时长  
    13.        mSampleLength = mSampleLength + (System.currentTimeMillis() - mSampleStart);  
    14.         //设置录音的状态为暂停的状态  
    15.         setState(PAUSE_STATE);  
    16.     }  

    由这两部操作也可以看出来,录音的方法接口为MediaRecorder。在录音的过程中,如果发生了暂停或者继续,要做的除了调用接口

    方法进行暂停或者继续的操作,还有就是对录音的时长需要进行一个计算,以及录音的状态进行一个设置。

    但不论是暂停或者继续,都是对一个已经存在的录音对象所进行的操作,所以相对还是很简洁的,但想要一个东西从无到有,也就是

    说这个创建对象的过程中需要考虑很多东西。所以在 对录音的两个简单的暂停或者继续进行分析后,接下里就开始分析开始录音的

    操作。也就是说我们学会了当对象存在时如何操作对象后,就来研究一下如何去新建一个对象。(先不考虑手机播放音乐、来电等其

    他audio的情况)。

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    开始录音流程图如下:

    开始录音的代码如下:

    [java] view plain copy
     
    1. private void startRecord() {  
    2.        //创建录音对象  
    3.       mRecorder = new MediaRecorder();  
    4.   
    5.        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
    6.        mRecorder.setAudioChannels(1);  
    7.        mRecorder.setAudioSamplingRate(1000);  
    8.        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);  
    9.        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
    10.        File sample = new File(Environment.getExternalStorageDirectory().toString()  
    11.                + "/SoundRecorder");  
    12.        if (!sample.exists()) {  
    13.            sample.mkdirs();  
    14.        }  
    15.   
    16.        if (!sample.canWrite()) {  
    17.            sample = new File("sdcard/sdcard");  
    18.        }  
    19.        file = new File(sample, "fang.aac");  
    20.        //设置文件存储位置  
    21.       mRecorder.setOutputFile(file.getAbsolutePath());  
    22.        try {  
    23.            mRecorder.prepare();  
    24.        } catch (IOException e) {  
    25.            e.printStackTrace();  
    26.            mRecorder.reset();  
    27.            mRecorder.release();  
    28.            mRecorder = null;  
    29.            Toast.makeText(getApplicationContext(), "exception", Toast.LENGTH_SHORT).show();  
    30.            return;  
    31.        }  
    32.        mRecorder.start();  
    33.   
    34.       //记录录音开始时间  
    35.        mSampleStart = System.currentTimeMillis();  
    36.       //更新ui  
    37.        updateTimeView();  
    38.   
    39.    }  

    停止录音代码如下

    [java] view plain copy
     
    1. private void stopRecord(){  
    2.        if (mRecorder == null){  
    3.            return;  
    4.        }  
    5.        mHandler.removeCallbacks(updateTime);  
    6.   
    7.        mRecorder.stop();  
    8.        mRecorder.reset();  
    9.        mRecorder.release();  
    10.        mSampleStart = 0;  
    11.        mRecorder = null;  
    12.   
    13.    }  

    <2>,播放录音playbutton

    播放录音代码如下:

    [java] view plain copy
     
    1. private void playRecord(){  
    2.   
    3.        mPlayer = new MediaPlayer();  
    4.   
    5.        try {  
    6.            mPlayer.setDataSource(file.getAbsolutePath());  
    7.   
    8.            mPlayer.prepare();  
    9.            mPlayer.start();  
    10.        } catch (IOException e) {  
    11.            e.printStackTrace();  
    12.        }  
    13.   
    14.    }  

    <3>,录音过程中检测分贝的方法为:

    [java] view plain copy
     
    1. mRecorder.getMaxAmplitude()  

    接下来贴出一张Android源码中关于录音频/视频的流程图,有需要的快点儿mark一下吧

    至于中止和继续录音,系统应用有可以使用的方法,但是不供三方应用使用,所以需要自己实现,其中涉及到编解码的问题,下篇再续!!!

    文章到此,基本上录音按钮的功能就分析完毕了。

    接下来做个demo来验证下录音按钮的功能

    ----------------------------

    版权声明:本文为博主原创文章,未经博主允许不得转载。
  • 相关阅读:
    SSL证书指令
    重启机器解决SSL都要输入密码问题
    Nginx + Apache 反向代理
    Ubuntu Nginx安装
    Nginx配置文件详解
    Linux SSL 双向认证 浅解
    SSL 双向认证
    linux ssl 双向认证
    ubuntu apache2 ssl配置
    vi编辑器命令
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7774141.html
Copyright © 2011-2022 走看看