You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 11(SDK30)录音权限配置问题求助

Android 11(SDK30)录音应用权限问题排查与解决

问题分析

你遇到的FileNotFoundException: open failed: EPERM (Operation not permitted)错误,根源并非单纯缺少权限,而是Android 11存储权限机制变化、权限申请逻辑缺陷、文件路径与目录处理错误共同导致的。

具体解决步骤

1. 修复权限申请逻辑

你当前使用RequestPermission单次申请单个权限,连续调用两次会导致请求覆盖,可能出现某个权限未成功申请的情况。需改用多权限申请:

// 替换原有的requestPermission变量
private val requestMultiplePermissions =
    registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
        val audioGranted = permissions[Manifest.permission.RECORD_AUDIO] ?: false
        val storageGranted = permissions[Manifest.permission.WRITE_EXTERNAL_STORAGE] ?: false
        isRecordable = audioGranted && storageGranted
        if (!isRecordable) {
            Log.d("ddddd", "录音或存储权限被拒绝")
        }
    }

// 修改onCreateView中的权限检查逻辑
override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = FragmentDetailBinding.inflate(inflater, container, false)

    val requiredPermissions = arrayOf(
        Manifest.permission.RECORD_AUDIO,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
    )
    val allGranted = requiredPermissions.all {
        ContextCompat.checkSelfPermission(requireContext(), it) == PackageManager.PERMISSION_GRANTED
    }
    if (!allGranted) {
        requestMultiplePermissions.launch(requiredPermissions)
    } else {
        isRecordable = true
    }
    return binding.root
}

2. 修复文件路径问题

  • 创建目标目录:代码中直接写入/Recordings目录,但该目录不存在时会导致文件创建失败,需先检查并创建目录。
  • 去除文件名非法字符:LocalDateTime.now().toString()包含冒号:,Android文件系统不允许路径中存在该字符,需替换为合法字符(如-)。

修改record函数:

fun record(){
    Log.d("ddddd",LocalDateTime.now().toString())
    if(!isRecordable)
        return

    // 创建Recordings目录
    val recordingsDir = File(Environment.getExternalStorageDirectory(), "Recordings")
    if (!recordingsDir.exists()) {
        val isCreated = recordingsDir.mkdirs()
        if (!isCreated) {
            Log.d("ddddd", "无法创建Recordings目录")
            return
        }
    }
    // 替换文件名中的非法字符
    val timeStr = LocalDateTime.now().toString().replace(":", "-")
    val mFileName = "${recordingsDir.absolutePath}/$timeStr test.3gp"
    
    recorder = MediaRecorder().apply {
        setAudioSource(MediaRecorder.AudioSource.DEFAULT)
        setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
        setOutputFile(mFileName)
        setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
        try {
            prepare()
            start()
        } catch (e: IOException) {
            println("录音启动失败:${e.message}")
            recorder?.release()
            recorder = null
            recording = false
            return
        }
    }
    recording=true
}

3. Android 11存储权限适配

Android 11(SDK30)引入了Scoped Storage,WRITE_EXTERNAL_STORAGE权限不再允许直接写入公共根目录下的自定义文件夹(如/Recordings)。如果坚持要写入该目录,需额外处理:

  • 在AndroidManifest.xml中添加特殊权限:
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        android:minSdkVersion="30" />
    
  • 在代码中检查并引导用户开启该权限:
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        if (!Environment.isExternalStorageManager()) {
            val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
            intent.data = android.net.Uri.parse("package:" + requireContext().packageName)
            startActivity(intent)
        }
    }
    

更推荐的方案:使用应用私有存储目录(无需额外权限)或MediaStore API写入公共媒体目录,避免申请特殊权限。例如使用私有目录:

// 应用私有存储目录
val recordingsDir = File(requireContext().getExternalFilesDir(Environment.DIRECTORY_MUSIC), "Recordings")

内容的提问来源于stack exchange,提问作者Adi_Hsiao_0410

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:05:15