当我尝试实现一个非常简单的需求时—— 下载图片并将其保存到本地存储 — 起初一切看起来都很正常。.
- 荣耀(Android 10)——运行正常
- Redmi(Android 11)——运行正常
- 小米(Android 13)——运行正常
- 三星(Android 13) 完全失败:存储权限对话框始终不会显示。
同样的代码,同样的功能,但一台运行 Android 13 的设备就是不显示权限提示。就这样,一个原本简单的“下载图片”任务,演变成了一场深入的调查。 作用域存储 和 管理外部存储.
这篇文章总结了我如何调整存储权限。 Android 11 及以上版本, 以及我如何处理不同版本之间的不同行为。.
1. 漏洞:存储权限对话框始终不显示(三星安卓 13)
要求很简单:
下载图片并保存到设备中,使其显示在图库中。.
我的三台测试设备如下:
- 荣耀 – 安卓 10 → 确定
- Redmi – Android 11 → 确定
- 小米 – Android 13 → 确定
但是, 运行 Android 13 的三星设备, 系统 从未显示过存储权限对话框, 无论我如何要求,都无济于事。.



起初我以为这只是另一个 OEM 厂商的怪癖,但在检查了 Android 各个版本之间的存储权限更改后,我意识到我所依赖的行为在 Android 13 上针对 SDK 33 时实际上已被弃用。.
2. 根本原因:WRITE/READ_EXTERNAL_STORAGE 在 Android 13 (SDK 33) 中已弃用
在旧版本的 Android 系统中,我们可以直接在清单文件中声明这两个权限:
读取外部存储写入外部存储
然后根据需要,在运行时请求它们。.
在 Android 13 (SDK 33) 上 targetSdkVersion = 33, 这种方法开始失效:
写入外部存储是 已弃用且实际上无用 在最新的 Android 版本上- 如果你添加
maxSdkVersion=32这些权限在 Android 11/12 上仍然有效。
但他们是 忽略 在 Android 13 上,目标版本为 33 - 同时,Play 商店要求新应用至少要面向 SDK 33。
因此,对于 Android 11 及更高版本,我们必须进行如下调整:
- 作用域存储
- 在某些情况下,还需要特殊许可:
管理外部存储
管理外部存储 赋予应用程序对所有共享存储内容(包括非媒体文件)的广泛访问权限。 不是 允许访问其他应用的私有目录,但 Google Play 仍然认为这是一项高度敏感的权限。.
为了支持不同的安卓版本,我最终将权限处理拆分为:
- Android 11 之前版本(API < 30) 旧式外部存储权限
- Android 11 及以上版本 – 范围存储 + 特殊处理
管理外部存储在严格需要的情况下
3. 逐步适应
3.1 在清单文件中声明 MANAGE_EXTERNAL_STORAGE
在 AndroidManifest.xml:
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />⚠️ 注意:因为
管理外部存储这是一个敏感权限,在 Google Play 上受到限制。如果您只是想保存图片,我稍后会介绍其他方法。.
3.2 检查是否已授予权限
我使用 简易权限 简化权限检查。.
private fun checkPer(activity: PreViewActivity): Boolean { return if (Build.VERSION.SDK_INT >= 30) { EasyPermissions.hasPermissions( activity, android.Manifest.permission.MANAGE_EXTERNAL_STORAGE ) } else { EasyPermissions.hasPermissions( activity, android.Manifest.permission.WRITE_EXTERNAL_STORAGE ) } }
- 在 Android 11+(API >= 30)我检查
管理外部存储 - 在 Android 10 及以下版本我还在检查
写入外部存储
这种分裂至关重要,因为 写入外部存储 新版本中运行方式已不再相同。.
3.3 当权限缺失时请求权限
如果未获得权限,我会根据系统版本以不同的方式请求权限。.
private fun requestStoragePermission(activity: PreViewActivity, curImg: Int) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+ – 重定向到系统"所有文件访问权限"设置页面 val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:" + activity.packageName) activity.startActivityForResult(intent, 200) } else { // Android 10 及以下 – 普通运行时权限 val perm = android.Manifest.permission.WRITE_EXTERNAL_STORAGE PaperThreeVariable.isToRequestPer = true EasyPermissions.requestPermissions( PermissionRequest.Builder( activity, 200, perm ).build() ) } }
- 在 Android 11+你不能直接“弹出”一个普通的运行时对话框
管理外部存储
您必须引导用户进入系统设置页面,让他们手动授予“所有文件访问权限”。. - 在 Android 10 及以下版本经典的运行时权限对话框仍然有效。.
3.4 处理权限回调
EasyPermissions 有助于连接 Activity 的回调函数和我们自己的逻辑:
override fun onRequestPermissionsResult( requestCode: Int, permissions: Array , grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) } override fun onPermissionsGranted(requestCode: Int, perms: MutableList ) { AppInitUtils().saveFreshAppImageToGallery(this, curImg) PaperThreeVariable.isToRequestPer = false } override fun onPermissionsDenied(requestCode: Int, perms: MutableList ) { PaperThreeVariable.isToRequestPer = false if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { AppSettingsDialog.Builder(this) .setRationale("此函数需要启用存储权限") .setNegativeButton("否") .setPositiveButton("是") .build() .show() } }
我在这里使用 EasyPermissions 的原因:
- 用户可以 永久否认 权限问题,导致重复请求静默失败
- EasyPermissions 让以下操作变得更简单:
- 检测“永久拒绝”状态
- 显示一个对话框,引导用户进行以下操作: 系统设置 → 应用权限 手动启用存储访问
获得许可后,我调用:
AppInitUtils().saveFreshAppImageToGallery(this, curImg)
实际保存图像并刷新图库。.
经过此次适配,三星 Android 13 设备终于与其他设备表现一致了。.
补充说明:我的小米设备显示为 Android 13,但 Android Studio 的“历史连接设备”却将其识别为 Android 12。这或许可以解释为什么在某些情况下它仍然可以工作——但这正是版本感知权限处理如此重要的原因。.
4. 关于 MANAGE_EXTERNAL_STORAGE 和 Google Play 限制
管理外部存储 功能强大:
它授予对以下内容的读/写权限 所有共享存储 在设备上。.
因此,Google Play 将其视为 高度敏感的许可:
- 它主要用于 文件管理器/备份/杀毒软件 类型应用
- 你必须提交使用理由才能使用它。
- 如果您的应用只是典型的消费者应用(例如,保存图片、简单下载),那么您的请求很可能不会被批准。 拒绝
所以,如果你的唯一要求是:
“将图片保存到图库并使其对用户可见。”
那么你应该 强烈建议避免 管理外部存储 相反:
- 使用 媒体商店 将图像插入系统媒体库
- 或者使用无需完全文件访问权限即可保存图像的 API。
有几种模式:
- 将图像保存到 Pictures/DCIM 目录
- 通知媒体扫描器或依赖 MediaStore,以便画廊可以读取它。
- 做所有这些 无需请求 MANAGE_EXTERNAL_STORAGE
对于内部或非 Play 商店分发(例如,企业内部应用商店),从技术上讲,您仍然可以使用 Environment.getExternalStorageDirectory(), 但我并不建议在 2025 年围绕这个主题设计一款新应用。.
5. 版本演进总结(Android 9 → 13)
为了将所有内容集中在一处,这里对外部存储和权限在不同版本中的行为方式进行了概述。.
Android 9 及以下版本(API 28 及更早版本)
- 权限:
读取外部存储写入外部存储
- 行为:
- 应用程序可以自由访问
/sdcard及其子目录 - 即使卸载了应用程序,该应用程序创建的文件仍会保留在设备上。
- 应用程序可以自由访问
- 典型方法:
- 直接在外部存储路径下进行读写操作
Android 10 (API 29) – 引入了作用域存储
- 权限:
读取外部存储仍然有效写入外部存储它仍然存在,但其有效范围已缩小。
- 行为:
- 作用域存储 引入:
- 应用程序仅限于其自身的应用程序专属目录。
Android/data/你的软件包名称/ - 直接访问其他应用程序的文件受到限制
- 应用程序仅限于其自身的应用程序专属目录。
- 媒体文件(图像、视频、音频)应通过以下方式访问: 媒体商店
requestLegacyExternalStorage=true可能会暂时保留旧的行为
(但从 Android 11 开始,此标志将被忽略)
- 作用域存储 引入:
- 推荐方法:
- 对于图像/视频/音频:使用 媒体商店
- 对于私人文件:请使用
getExternalFilesDir()或者getDataDir()
Android 11 (API 30) – 强制执行作用域存储
- 权限:
读取外部存储有效,但仅适用于 MediaStore 管理的媒体。写入外部存储有效地 过时的 用于一般外部存储管理外部存储为特殊的“所有文件访问”用例而引入
- 行为:
requestLegacyExternalStorage=true不再有效;作用域存储是 始终开启- 访问
/sdcard/root 权限被阻止 - 应用程序只能:
- 访问他们自己的私人目录
- 通过以下方式访问共享媒体 媒体商店
- 推荐方法:
- 对于典型应用程序:
- 使用 MediaStore 或 新加坡武装部队 (
操作_打开文档,操作_创建文档)针对用户选择的文件
- 使用 MediaStore 或 新加坡武装部队 (
- 仅考虑
管理外部存储如果你的应用确实是一款文件管理器、备份工具、安全应用等等。.
- 对于典型应用程序:
Android 13 (API 33) – 媒体权限拆分
- 权限:
读取媒体图像– 访问图像读取媒体视频– 访问视频读取媒体音频– 访问音频
- 行为:
- 媒体权限是 细粒:
- 用户可以仅授予图像访问权限、仅授予视频访问权限等等。.
- Android 11 中的作用域存储规则仍然有效。
- 媒体权限是 细粒:
- 推荐方法:
- 请申请您需要的具体媒体授权:
- 例如,如果您只处理图像,则只需请求即可。
读取媒体图像
- 例如,如果您只处理图像,则只需请求即可。
- 做 不是 要求
读取外部存储在 Android 13 及更高版本中,该功能已被新的媒体权限取代。
- 请申请您需要的具体媒体授权:
快速矩阵(概念图)
- Android 9 及以下版本
- 对外部存储的访问范围很广,由 READ/WRITE_EXTERNAL_STORAGE 控制。
- Android 10
- 引入了范围存储,但也有退路(
请求旧版外部存储)
- 引入了范围存储,但也有退路(
- Android 11
- 强制执行范围存储,移除旧式交换机
管理外部存储出现但受到严格限制
- Android 13
- 媒体访问分为
READ_MEDIA_*权限 - 相同的作用域存储规则,但用户控制更加细粒度。
- 媒体访问分为
6. 要点总结
- 不要以为“在一台 Android 13 设备上运行正常”就意味着它在所有设备上都能正常运行;OEM 厂商和系统报告可能存在不一致的情况。.
- 为了 Android 11+, 可以从以下角度思考:
- 应用私有目录 + 媒体库 + SAF, 不是“生的”。
/sdcard使用权”
- 应用私有目录 + 媒体库 + SAF, 不是“生的”。
- 对待
管理外部存储作为一个 最后手段 尤其对于某些特定类型的应用,如果您计划在 Google Play 上发布,那就更需要注意了。. - 始终进行测试 多款设备和安卓版本, 尤其是在权限和存储方面。.
本文源于我在实际 Android 项目(包括运行 Android 13 的三星设备)中的调试和调整。GPT 仅协助翻译和润色文字;所有技术内容和决策均出自本人。.


