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
相关产品推荐
相关产品推荐

