Kotlin中读写外部存储权限请求无效问题排查
解决外部存储权限弹窗不显示的问题
以下是针对你遇到的权限弹窗不出现问题的具体分析和修复方案:
1. 权限请求逻辑错误
你的checkStoragePermissions方法中,shouldShowRequestPermissionRationale的判断逻辑存在逻辑漏洞,导致部分场景下无法触发权限请求弹窗。正确的逻辑应该是:当权限未授予时,先判断是否需要向用户解释权限用途(用户之前拒绝过但未勾选“不再询问”),如果需要则先展示说明,否则直接发起权限请求。
修正后的checkStoragePermissions方法:
private fun checkStoragePermissions() { val hasReadPermission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED val hasWritePermission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED if (!hasReadPermission || !hasWritePermission) { val needShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.READ_EXTERNAL_STORAGE) || ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) if (needShowRationale) { // 弹出提示框向用户解释权限用途 AlertDialog.Builder(this) .setTitle("需要存储权限") .setMessage("为了完成数据备份/恢复,需要访问您的外部存储") .setPositiveButton("确定") { _, _ -> requestPermissions( arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE), ALL_PERMISSION ) } .setNegativeButton("取消", null) .show() } else { // 直接发起权限请求 requestPermissions( arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE), ALL_PERMISSION ) } } else { // 权限已授予,执行备份/恢复操作 if (executeBackup) backupFromDBData(WeakReference(this)).execute() else restore_data() } }
2. Android 11+ 分区存储限制
从Android 11(API 30)开始,系统引入分区存储机制,WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限的作用被大幅限制。如果你的备份文件需要保存到公共目录(如Downloads、Documents),仅靠这两个权限无法正常访问,需要申请MANAGE_EXTERNAL_STORAGE特殊权限。
注意:MANAGE_EXTERNAL_STORAGE权限不能通过requestPermissions请求,必须引导用户跳转到系统设置页面手动开启:
private const val MANAGE_STORAGE_REQUEST_CODE = 1002 private fun checkManageExternalStoragePermission() { 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 = Uri.parse("package:${packageName}") startActivityForResult(intent, MANAGE_STORAGE_REQUEST_CODE) } else { // 权限已开启,执行操作 if (executeBackup) backupFromDBData(WeakReference(this)).execute() else restore_data() } } }
在onActivityResult中处理权限开启结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == MANAGE_STORAGE_REQUEST_CODE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Environment.isExternalStorageManager()) { if (executeBackup) backupFromDBData(WeakReference(this)).execute() else restore_data() } else { Toast.makeText(this, "需要开启全文件访问权限以完成操作", Toast.LENGTH_SHORT).show() } } } }
3. 权限回调逻辑不严谨
你的onRequestPermissionsResult中仅检查了第一个权限的授予状态,应该确保所有请求的权限都被授予:
override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { ALL_PERMISSION -> { val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED } if (allGranted) { if (executeBackup) backupFromDBData(WeakReference(this)).execute() else restore_data() } else { Toast.makeText(this, "存储权限未全部授予,无法完成操作", Toast.LENGTH_SHORT).show() } } } }
额外建议
优先考虑使用应用专属外部存储目录存储备份文件,这样无需申请任何存储权限,更符合系统隐私规范:
// 获取应用专属外部存储目录 val backupDir = File(getExternalFilesDir(null), "backups") if (!backupDir.exists()) { backupDir.mkdirs() } // 备份文件保存路径 val backupFile = File(backupDir, "db_backup.db")
内容的提问来源于stack exchange,提问作者ricP87
相关产品推荐
相关产品推荐

