这是一个简单的android 录音单元功能实现的源码,不涉及其他线程或服务之类的深入知识,详细针对 mediaRecorder 实现录音功能做记录和解析。
一 静态权限
现在的Android机已经普遍都是6.0以上的系统了,所以很多权限是需要动态申请的,这里录音权限就需要进行动态申请,我们把需要的权限先在 manifest.xml
文件中静态声明一下
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>
用到的就是上面这三个,包括存储空间的读,写,录音权限
这里只是静态权限的申请,6.0以上的系统还需要动态申请权限,后面的代码会逐步解析。
二 录音文件存放的位置
这里要说明的是,从Android 7.0 之后,系统加强了SD卡的权限管理,即使App声明了完整的SD卡操作权限,系统仍然默认禁止该App访问外部存储。不过系统默认关闭的存储只是外部存储的公共空间,而外部存储的私有空间依然可以正常读写。这个私有空间是只有应用自己才可访问的专享空间,而当这个APP卸载时这个空间也就一起被清理掉了。
在网上很多源码中提到使用 Environment.getExternalStroagePublicDirector 方法获取文件的存放路径,但其实这个方法在比较高版本的SDK中已经废弃了,当使用AS写代码时会有废弃的提示。因为这个方法返回的是公共空间的地址,App自身要操作这个空间很多时候是无法使用的。
那么我们就要获取App的私有空间地址,这里要用到的方法就是 getExternalFilesDir() 。这个方法返回的是一个File类的对象
简单的示例如下
var privatePath = getExternalFilesDir(null).toString()
这个 privatePath 获取的路径位于 “外部存储根目录/Android/data/应用包名/files” 下,注意这里最后是没有 “ / ” 的,也就是说后期要组合一些其他的下级文件路径不要忘了给它后面加“ / ”
三 录音API mediaRecorder 的使用
这个的用法还是挺简单的,下面直接通过源码说明
(1)布局文件源码
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tv_path"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_start"
android:text="开始"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_stop"
android:enabled="false"
android:text="停止"/>
</LinearLayout>
这个 id为 tv_path 的TextView 用于显示存储路径
(2)MainActivity.kt 源码
package com.example.recorder
import android.Manifest
import android.content.pm.PackageManager
import android.media.MediaRecorder
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.toast
import java.io.File
import java.io.IOException
class MainActivity : AppCompatActivity() {
private var sdcardfile : File? = null
private var recorder : MediaRecorder? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getSDCardFile()
btn_start.setOnClickListener {
if(checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED){
requestPermissions(arrayOf<String>(Manifest.permission.RECORD_AUDIO),1)
}else{
startRecord()
}
}
btn_stop.setOnClickListener{
stopRecord()
}
}
//获取存储路径
private fun getSDCardFile(){
if(Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED){
sdcardfile = getExternalFilesDir(null)
tv_path.text = sdcardfile.toString()
}else{
toast("未找到内存卡")
}
}
//开始录音
private fun startRecord(){
recorder = MediaRecorder()
//设置音频源
recorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
//设置输出格式
recorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
//设置音频编码
recorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
try{
var file = File.createTempFile("录音_",".amr",sdcardfile)
//设置录音保存路径
recorder!!.setOutputFile(file)
//准备和启动录制音频
recorder!!.prepare()
recorder!!.start()
}catch (e : IOException){
e.printStackTrace()
}
btn_start.isEnabled = false
btn_stop.isEnabled = true
}
//停止录音
private fun stopRecord(){
try{
recorder!!.stop()
recorder!!.release()
recorder = null
}catch (e:Exception){
e.printStackTrace()
}
btn_start.isEnabled = true
btn_stop.isEnabled = false
}
//动态申请权限的回调函数
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if(requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
startRecord()
}else{
toast("用户拒绝了权限")
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
以上源码还是很容易理解的,录音的流程如下
(1) 声明 MediaRecorder 的实例 recorder = MediaRecorder()
(2) 设置音频源 recorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
(3) 设置输出格式 recorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
(4) 设置音频编码 recorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
(5) 设置输出路径 recorder!!.setOutputFile(file)
(6) 准备和启动录制音 recorder!!.prepare()
recorder!!.start()
关闭录音的方法也很简单,直接看源码就完全能理解
四 动态权限的申请
这里是一个需要理解的地方,不然很多东西没法做,我们看到 btn_start 监听器下的源码中有一段程序
if(checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED){
requestPermissions(arrayOf<String>(Manifest.permission.RECORD_AUDIO),1)
}
这里就是先判断App是否有需要的权限,如果我们需要的权限没有开通,就通过 requestPermissions()
方法申请开通该权限.
程序运行到这里后,用户的界面就会弹出授权提示框.
用户不管是点击了同意授权还是不同意,程序就会执行回调函数onRequestPermissionsResult()
.
在该回调函数中,如果用户同意了该授权,就可以继续做录音的程序,如果不同意,我们可以输出一些提示。
以上纯手工码字,如发现错误请不吝赐教,我会及时修改