Android 13/14存储权限自动拒绝问题求助(适配Android 5旧应用)
问题分析与解决方案
核心问题
Android 13(API 33)及以上版本彻底重构了存储权限模型:WRITE_EXTERNAL_STORAGE 权限被废弃,系统会直接拒绝该权限的请求;同时针对媒体文件的读写权限拆分为 READ_MEDIA_* 和 WRITE_MEDIA_* 系列权限。你的代码仍在请求旧的存储权限,导致高版本系统直接拒绝。
步骤1:修正权限清单
首先调整 AndroidManifest.xml 中的权限声明,适配不同API级别:
<!-- 仅在Android 12及以下版本生效的旧存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13+ 媒体文件权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.WRITE_MEDIA_IMAGES" />
注:
WRITE_MEDIA_IMAGES仅在需要修改/删除其他应用创建的图片时才需要;如果只是保存自己应用生成的截图到媒体库,Android 13+ 允许直接通过MediaStoreAPI写入,无需申请该权限。
步骤2:修改权限请求逻辑
更新 MainActivity 中的权限判断与请求代码,根据系统版本动态选择权限:
private var screenshotManager: ScreenshotManager? = null private val REQUEST_CODE_STORAGE_PERMISSION = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 根据API版本选择要请求的权限 val targetPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Android 13+ 请求媒体权限 arrayOf(Manifest.permission.WRITE_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_IMAGES) } else { // Android 12及以下 请求旧存储权限 arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) } if (ContextCompat.checkSelfPermission(this, targetPermissions.first()) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, targetPermissions, REQUEST_CODE_STORAGE_PERMISSION) } else { hasStoragePermission = true } screenshotManager = ScreenshotManagerBuilder(this) .withPermissionRequestCode(888) .build() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_CODE_STORAGE_PERMISSION) { hasStoragePermission = grantResults.all { it == PackageManager.PERMISSION_GRANTED } if (hasStoragePermission) { Toast.makeText(this, "权限已授予", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "权限被拒绝,截图保存功能将受限", Toast.LENGTH_SHORT).show() } } }
步骤3:适配Screenshotty的存储路径
如果权限请求后仍无法保存截图,大概率是Screenshotty默认使用了公共外部存储目录(如DCIM),而Scoped Storage限制了直接写入。推荐改为保存到应用私有外部存储目录,这个路径无需任何存储权限:
// 创建应用私有图片目录 val privatePicDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES) // 自定义截图保存路径(需查看Screenshotty文档确认是否支持自定义路径) screenshotManager = ScreenshotManagerBuilder(this) .withPermissionRequestCode(888) // 如果Screenshotty支持自定义保存位置,添加以下配置(示例) .withSaveLocation(privatePicDir) .build()
如果Screenshotty不支持自定义路径,可以在截图回调中自行处理文件写入:
screenshotManager?.takeScreenshot(object : ScreenshotCallback { override fun onScreenshotTaken(screenshot: Screenshot) { // 将截图Bitmap写入应用私有目录 val outputFile = File(privatePicDir, "screenshot_${System.currentTimeMillis()}.png") FileOutputStream(outputFile).use { stream -> screenshot.bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) } // 可选:将截图插入媒体库,让用户能在相册看到 MediaStore.Images.Media.insertImage(contentResolver, outputFile.path, outputFile.name, "") } override fun onFailed(e: Exception) { // 处理截图失败逻辑 } })
额外说明
- 读取用户选择的JSON文件:你当前的逻辑没问题,因为通过文件选择器获取的Uri可以直接通过
ContentResolver读取,无需存储权限,这也是为什么你在高版本中仍能正常读取文件。 - 避免使用
MANAGE_EXTERNAL_STORAGE:除非你的应用是文件管理器类应用,否则Google Play会拒绝该权限的上架申请。
内容的提问来源于stack exchange,提问作者daydr3am3r
相关产品推荐
相关产品推荐

