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

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+ 允许直接通过 MediaStore API写入,无需申请该权限。


步骤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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 15:43:19