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

【Android6.0源码分析之录音功能】千金一刻莫空度,老大无成空自伤。这篇文章主要讲述Android6.0源码分析之录音功能相关的知识,希望能为你提供帮助。
本文转载自:http://blog.csdn.net/zrf1335348191/article/details/54949549
从现在开始一周时间研究录音,下周出来一个完整的博客,监督,激励!!!
2017-02-09--------2017-02-17
 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
android源码录音功能说起来似乎也很简单,只不过就是一个录音的功能然后进行了一个保存的操作。为什么要研究这个呢?毕竟现
在语音通话、直播亦或者是语音助手比较流行,其中其实最基础的还是对录音的一些处理,所以还是有必要研究一下的。
说起来功能简单,但其实单单是录音功能其中又夹杂着一些别的东西,比如UI的实时更新,电话等各种状态的监控,音量大小的监
控。录音的保存也涉及到往手机中写数据,以及以何种格式写数据,比如当前流行的直播,音频流的传输到底以何种格式,手机可以
播放什么样的格式,这些都会涉及。但是手机的原生系统应用录音机不支持文件的播放。
 
代码所在目录为android\\packages\\apps\\SoundRecorder

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

文章图片

先从编译开始说起
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
Android6.0源码分析之录音功能

文章图片

 
对应用进行整体浏览后发现录音所涉及到的知识由以下几个
1> ,录音计时
2> ,录音音量UI设计
3> ,开始,暂停,继续录音
4> ,停止录音
5> ,播放录音
6> ,以某种格式保存录音,文件类型支持amr,3gpp,aac,wav
7> ,显示录音文件列表
其实总的来说也就是录音然后以某种格式保存的功能,只是在录音的过程中需要根据不同情况对UI进行一个更新。
接下来对单个功能进行分析之前首先是对各个view的id进行一个标记,方便以后对UI上做修改
 
Android6.0源码分析之录音功能

文章图片

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

文章图片

 
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录音,中止,继续
对于录音按钮的点击处理逻辑如下:
Android6.0源码分析之录音功能

文章图片

 
也就是说在录音按钮时如果想要开启录音首先要经过两次判断
第一是判断是否是中止状态,也就是说录音中止此时点击会继续录音。
第二是判断是否是正在进行录音,如果正在进行,则点击时会中止正在进行的录音
经过以上两步的判断后才会进入录音的准备,但至于录音是否需能够开启成功还需要看后续的判断。
 
那么如果当前录音处于中止状态的话该如何继续录音呢???
其实继续录音就三件事,一是开启录音,二是对录音进行开始计时,第三就是设置下当前的录音状态为正在录音
 
[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的情况)。
 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
开始录音流程图如下:
 
Android6.0源码分析之录音功能

文章图片

 
 
开始录音的代码如下:
 
[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一下吧
Android6.0源码分析之录音功能

文章图片

至于中止和继续录音,系统应用有可以使用的方法,但是不供三方应用使用,所以需要自己实现,其中涉及到编解码的问题,下篇再续!!!
 
文章到此,基本上录音按钮的功能就分析完毕了。
接下来做个demo来验证下录音按钮的功能
----------------------------
 
版权声明:本文为博主原创文章,未经博主允许不得转载。

    推荐阅读