Android6.0源码分析之录音功能(一)【转】
本文转载自:http://blog.csdn.net/zrf1335348191/article/details/54949549
从现在开始一周时间研究录音,下周出来一个完整的博客,监督,激励!!!
2017-02-09--------2017-02-17
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Android源码录音功能说起来似乎也很简单,只不过就是一个录音的功能然后进行了一个保存的操作。为什么要研究这个呢?毕竟现
在语音通话、直播亦或者是语音助手比较流行,其中其实最基础的还是对录音的一些处理,所以还是有必要研究一下的。
说起来功能简单,但其实单单是录音功能其中又夹杂着一些别的东西,比如UI的实时更新,电话等各种状态的监控,音量大小的监
控。录音的保存也涉及到往手机中写数据,以及以何种格式写数据,比如当前流行的直播,音频流的传输到底以何种格式,手机可以
播放什么样的格式,这些都会涉及。但是手机的原生系统应用录音机不支持文件的播放。
代码所在目录为android\packages\apps\SoundRecorder
先从编译开始说起
chapter one 录音机编译脚本文件-Android.mk
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE_TAGS := optional
- LOCAL_SRC_FILES := $(call all-subdir-java-files)
- LOCAL_PACKAGE_NAME := SoundRecorder
- LOCAL_PRIVILEGED_MODULE := true
- 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
配置文件会告诉我们应用的入口,应用所需的权限以及应用的各种组件,看一个应用的复杂程度其实看配置文件即可
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.soundrecorder">
- <original-package android:name="com.android.soundrecorder" />
- <uses-permission android:name="android.permission.RECORD_AUDIO" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application android:label="@string/app_name"
- android:icon="@drawable/ic_launcher_soundrecorder"
- android:usesCleartextTraffic="false">
- <activity android:name="SoundRecorder"
- android:configChanges="orientation|screenSize|keyboardHidden"
- android:screenOrientation="unspecified"
- android:clearTaskOnLaunch="true"
- android:theme="@style/Theme.SoundRecorder">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.provider.MediaStore.RECORD_SOUND" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
- </application>
- </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来调起录音界面,亲测有效~~
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方法。
- /*
- * Handle the buttons.
- */
- public void onClick(View button) {
- if (!button.isEnabled())
- return;
- if (Build.VERSION.SDK_INT >= 23) {
- String[] operationPermissionNames = getOperationPermissionName(button.getId());
- if (operationPermissionNames == null ||
- checkOperationPermission(operationPermissionNames, button.getId()))
- processClickEvent(button.getId());
- } else {
- processClickEvent(button.getId());
- }
- }
当进行button的点击事件进行处理时涉及到一个6.0权限的问题,首先要保证权限已经申请成功,然后在processClickEvent再对各种
点击事件进行处理。
<1>,recordButton录音,中止,继续
对于录音按钮的点击处理逻辑如下:
也就是说在录音按钮时如果想要开启录音首先要经过两次判断
第一是判断是否是中止状态,也就是说录音中止此时点击会继续录音。
第二是判断是否是正在进行录音,如果正在进行,则点击时会中止正在进行的录音
经过以上两步的判断后才会进入录音的准备,但至于录音是否需能够开启成功还需要看后续的判断。
那么如果当前录音处于中止状态的话该如何继续录音呢???
其实继续录音就三件事,一是开启录音,二是对录音进行开始计时,第三就是设置下当前的录音状态为正在录音
- public void resumeRecording() {
- if (mRecorder == null) {
- return;
- }
- try {
- //mRecorder为MeidaRecorder对象,开始录音
- mRecorder.start();
- } catch (RuntimeException exception) {
- setError(INTERNAL_ERROR);
- Log.e(TAG, "Resume Failed");
- }
- //开始录音计时
- mSampleStart = System.currentTimeMillis();
- //设置当前的状态为正在录音
- setState(RECORDING_STATE);
- }
那如果当前录音处于正在录音的状态该如何中止录音呢?????
对照上述继续录音的代码可以看到中止录音进行了以下操作:一是暂停当前的录音,二是对本次录音(包括多次暂停和继续)的总时
长进行一个累加并记录入mSampleLength,三是设置录音的状态为暂停的状态
- public void pauseRecording() {
- if (mRecorder == null) {
- return;
- }
- try {
- //mRecorder为MediaRecorder对象,暂停录音
- mRecorder.pause();
- } catch (RuntimeException exception) {
- setError(INTERNAL_ERROR);
- Log.e(TAG, "Pause Failed");
- }
- //记录从开始录音到现在的总的录音时长
- mSampleLength = mSampleLength + (System.currentTimeMillis() - mSampleStart);
- //设置录音的状态为暂停的状态
- setState(PAUSE_STATE);
- }
由这两部操作也可以看出来,录音的方法接口为MediaRecorder。在录音的过程中,如果发生了暂停或者继续,要做的除了调用接口
方法进行暂停或者继续的操作,还有就是对录音的时长需要进行一个计算,以及录音的状态进行一个设置。
但不论是暂停或者继续,都是对一个已经存在的录音对象所进行的操作,所以相对还是很简洁的,但想要一个东西从无到有,也就是
说这个创建对象的过程中需要考虑很多东西。所以在 对录音的两个简单的暂停或者继续进行分析后,接下里就开始分析开始录音的
操作。也就是说我们学会了当对象存在时如何操作对象后,就来研究一下如何去新建一个对象。(先不考虑手机播放音乐、来电等其
他audio的情况)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
开始录音流程图如下:
开始录音的代码如下:
- private void startRecord() {
- //创建录音对象
- mRecorder = new MediaRecorder();
- mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- mRecorder.setAudioChannels(1);
- mRecorder.setAudioSamplingRate(1000);
- mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
- File sample = new File(Environment.getExternalStorageDirectory().toString()
- + "/SoundRecorder");
- if (!sample.exists()) {
- sample.mkdirs();
- }
- if (!sample.canWrite()) {
- sample = new File("sdcard/sdcard");
- }
- file = new File(sample, "fang.aac");
- //设置文件存储位置
- mRecorder.setOutputFile(file.getAbsolutePath());
- try {
- mRecorder.prepare();
- } catch (IOException e) {
- e.printStackTrace();
- mRecorder.reset();
- mRecorder.release();
- mRecorder = null;
- Toast.makeText(getApplicationContext(), "exception", Toast.LENGTH_SHORT).show();
- return;
- }
- mRecorder.start();
- //记录录音开始时间
- mSampleStart = System.currentTimeMillis();
- //更新ui
- updateTimeView();
- }
停止录音代码如下
- private void stopRecord(){
- if (mRecorder == null){
- return;
- }
- mHandler.removeCallbacks(updateTime);
- mRecorder.stop();
- mRecorder.reset();
- mRecorder.release();
- mSampleStart = 0;
- mRecorder = null;
- }
<2>,播放录音playbutton
播放录音代码如下:
- private void playRecord(){
- mPlayer = new MediaPlayer();
- try {
- mPlayer.setDataSource(file.getAbsolutePath());
- mPlayer.prepare();
- mPlayer.start();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
<3>,录音过程中检测分贝的方法为:
- mRecorder.getMaxAmplitude()
接下来贴出一张Android源码中关于录音频/视频的流程图,有需要的快点儿mark一下吧
至于中止和继续录音,系统应用有可以使用的方法,但是不供三方应用使用,所以需要自己实现,其中涉及到编解码的问题,下篇再续!!!
文章到此,基本上录音按钮的功能就分析完毕了。
接下来做个demo来验证下录音按钮的功能
----------------------------
Android6.0源码分析之录音功能(一)【转】的更多相关文章
- android6.0源码分析之Camera API2.0下的Capture流程分析
前面对Camera2的初始化以及预览的相关流程进行了详细分析,本文将会对Camera2的capture(拍照)流程进行分析. 前面分析preview的时候,当预览成功后,会使能ShutterButto ...
- Android6.0 源码修改之 Contacts应用
一.Contacts应用的主界面和联系人详情界面增加顶部菜单添加退出按钮 通过Hierarchy View 工具可以发现 主界面对应的类为 PeopleActivity 联系人详情界面对应的类为 Qu ...
- Android6.0 源码修改之 仿IOS添加全屏可拖拽浮窗返回按钮
前言 之前写过屏蔽系统导航栏功能的文章,具体可看Android6.0 源码修改之屏蔽导航栏虚拟按键(Home和RecentAPP)/动态显示和隐藏NavigationBar 在某些特殊定制的版本中要求 ...
- AFNetWorking3.0源码分析
分析: AFNetWorking(3.0)源码分析(一)——基本框架 AFNetworking源码解析 AFNetworking2.0源码解析<一> end
- Solr5.0源码分析-SolrDispatchFilter
年初,公司开发法律行业的搜索引擎.当时,我作为整个系统的核心成员,选择solr,并在solr根据我们的要求做了相应的二次开发.但是,对solr的还没有进行认真仔细的研究.最近,事情比较清闲,翻翻sol ...
- 在Ubuntu Server14.04上编译Android6.0源码
此前编译过Android4.4的源码,但是现在Android都到了7.0的版本,不禁让我感叹Google的步伐真心难跟上,趁这周周末时间比较充裕,于是在过去的24小时里,毅然花了9个小时编译了一把An ...
- Solr4.8.0源码分析(25)之SolrCloud的Split流程
Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大 ...
- Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)
Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了 ...
- Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四)
Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四) 题记:本来计划的SolrCloud的Recovery策略的文章是3篇的,但是没想到Recovery的内容蛮多的,前面 ...
随机推荐
- MongoDB安装与配置启动
1.下载安装包.mongodb-linux-x86_64-rhel62-3.6.3.tgz 2.解压.修改名字. 3.修改配置文件: # mongodb.conf #where to loglogpa ...
- Buffer.isBuffer()详解
Buffer.isBuffer(obj) obj {Object} 返回:{Boolean} 如果 obj 是一个 Buffer 则返回 true.
- Atcoder regular Contest 073(C - Sentou)
Atcoder regular Contest 073(C - Sentou) 传送门 每个人对开关的影响区间为a[i]--a[i]+t,因此此题即为将所有区间离散化后求所有独立区间的长度和 #inc ...
- bootstrap删除模态框弹出并询问是否删除【通用删除模态框】
普通的询问是否删除的对话框比较low,可以利用bootstrap的模态框代替普通的对话框来实现删除. 效果: 点删除的时候弹出模态框询问是否删除,点确认的时候将需要删除的ID传到后台进行删除. 过程 ...
- Less Time, More profit 最大权闭合子图(最大流最小割)
The city planners plan to build N plants in the city which has M shops. Each shop needs products fro ...
- winform总结6=>线程和委托的关系
基础类: using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys ...
- [bzoj2251][2010BeiJing Wc]外星联络_后缀数组
外星联络 bzoj-2251 2010-BeiJing Wc 题目大意:题目链接. 注释:略. 想法: 这咋做啊????一看数据范围才$3\cdot 10^3$. 建立后缀数组. 所以我们将所有后缀排 ...
- MongoDB小结09 - update【定位修改器】
如果要操作数组中的值,可以用值在数组中的位置当做参数来删除 db.user.update({"name":"codingwhy.com"},{"$se ...
- 基于cocos2d-x-3.2学习Box2D(一)
cocos版本号:cocos2d-x-3.2 环境:Win7+VS2013 因为一些太底层的实现我如今的能力学习不到,仅仅能做一些简单的笔记,供以后翻阅.假设别人可以得到帮助,莫大的荣幸. 一.创建世 ...
- ArcGIS 教程:Workflow Manager 高速浏览
应用程序概述 Workflow Manager 用户界面提供了用于在整个作业的生命周期中创建和管理作业的工具. 下面全部信息将会在本帮助文档的兴许章节中进行具体的说明. 文件菜单 新建 - 在系统中创 ...