一、Bug简述
一个很普通的需求,需要下载图片到本地,我的三个测试机(荣耀Android10,红米 11 和小米Android 13都没有问题)。
然后,主角登场了,测试的三星Android 13 死活拉不起存储权限弹窗。
想了下,三星的系统可能和小米的系统做了些区别。于是就是看了下存储权限的版本更迭,却是发现了些骚东西。
二、原因
很早以前,一直都是在manifest申明这两个权限就可以了,但是现在会有下面的警告!
这便是错误的原因:
可以得知,在Android 13(sdkversion为33)的系统中,已经被废弃了!!!
但是如果你给Write和Read权限加上maxSdkVersion=32,他在11,12上是没有问题的,但是最新的App要求 target sdkversion必须是33了。
所以,在此就必须做一个适配:
Android 11 里将引入一个特别的权限叫做 MANAGE_EXTERNAL_STORAGE,该权限将授权读写所有共享存储内容,这也将同时包含非媒体类型的文件。但是获得这个权限的应用还是无法访问其他应用的应用专属目录 (app-specific directory),无论是外部存储还是内部存储。
那么,为了兼容Android版本,我就必须在Android11之前和之后分开做申请才会有用。
三、步骤
1).申明权限
manifest文件里:
android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> 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 ) } } 未获得权限,申请权限 private fun aaa(activity: PreViewActivity, curImg: Int) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:" + activity.packageName) activity.startActivityForResult(intent, 200) } else { val perm = android.Manifest.permission.WRITE_EXTERNAL_STORAGE PaperThreeVariable.isToRequestPer = true EasyPermissions.requestPermissions( PermissionRequest.Builder( activity, 200, perm ) .build() ) } } 权限申请回调 override fun onRequestPermissionsResult( requestCode: Int, permissions: Array ) { 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("This function requires storage permission to be enabled") .setNegativeButton("No") .setPositiveButton("Yes") .build().show() } } 因为有时候用户会拒绝权限且永久弹窗,所以为了方便我就用了EasyPermission这个库,拒绝的情况下,弹窗跳转系统权限设置页面去让用户选择开启权限。 这样,就OK啦!(至于我的小米为啥Android 13也能正常拉起,我也不知道具体原因,但是在Android Studio的历史链接设备中,我发现它识别我的手机系统为Android 12,真是百思不得其解) 本文参考了郭霖大神的一篇文章,当时是想看下 Scoped Storage这个属性的内容,接过发现了Android 11的变更。 Android 11新特性,Scoped Storage又有了新花样 仅做个人工作总结,内容肯定不够全面,如有问题,欢迎大佬指正! Android Permission 权限申请,EasyPermission和其他三方库_安卓权限申请_&岁月不待人&的博客-CSDN博客 补充: 因为 MANAGE_EXTERNAL_STORAGE,该权限将授权读写所有共享存储内容,所以在谷歌中属于敏感权限,不容易通过,如果只是想保存图片,则可以通过下面的博客中的方法去保存 Android 保存图片并刷新相册(无需权限)-CSDN博客 Android Gilde获取网络图片显示保存路径并转化为bitmap-CSDN博客 四、总结性补充 2025.2.6 1. Android 9 及以下(API 级别 28 及以下) 权限要求: 需要 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限才能访问外部存储。 特点: 可以访问 /sdcard 目录及其子目录的所有文件。应用卸载后,其创建的文件仍然存在。 2. Android 10(API 级别 29)—— 引入分区存储(Scoped Storage) 权限要求: READ_EXTERNAL_STORAGE 仍然适用。WRITE_EXTERNAL_STORAGE 仍适用,但影响范围缩小。 特点: 分区存储:应用只能访问自己应用目录下的文件 /Android/data/your.package.name/。无法直接操作其他应用创建的文件(除了 MediaStore API 提供的公共媒体文件)。仍可使用 requestLegacyExternalStorage=true 来暂时保持旧行为(Android 11 及以上无效)。 适配方案: 对于 图片、视频、音频,应使用 MediaStore 进行访问和存储。对于 私有文件,应存放在 getExternalFilesDir() 或 getDataDir() 目录下。 3. Android 11(API 级别 30)—— 强制分区存储 权限要求: READ_EXTERNAL_STORAGE 仍然适用(仅限于 MediaStore)。WRITE_EXTERNAL_STORAGE 彻底失效,无法申请。访问某些特定目录(如 Documents/Download)需要 MANAGE_EXTERNAL_STORAGE 权限(敏感权限,Google Play 限制使用)。 特点: requestLegacyExternalStorage=true 不再有效,强制启用 分区存储。禁止访问 /sdcard/ 根目录,只能访问 MediaStore 提供的共享媒体文件(图片、视频、音频)。应用卸载后,应用私有目录下的文件会被自动清理。 适配方案: 普通应用:只能访问 MediaStore 提供的媒体文件(推荐使用 ACTION_OPEN_DOCUMENT 选择文件)。特殊情况: 需要 MANAGE_EXTERNAL_STORAGE 权限,但 Google Play 限制其使用,必须填写申请理由,除非是文件管理类 App,否则会被拒绝。如果是 非商店渠道(如企业内部发布),可以直接使用 Environment.getExternalStorageDirectory()。 4. Android 13(API 级别 33)—— 细化媒体权限 权限要求: READ_MEDIA_IMAGES(访问图片)READ_MEDIA_VIDEO(访问视频)READ_MEDIA_AUDIO(访问音频) 特点: 细化媒体文件权限,用户可以选择性地授予权限,而不必一次性授予所有媒体访问权限。其他限制和 Android 11 保持一致。 适配方案: 需要分别请求 READ_MEDIA_IMAGES、READ_MEDIA_VIDEO 和 READ_MEDIA_AUDIO 权限,而不是 READ_EXTERNAL_STORAGE。 版本访问私有目录访问公共媒体文件访问其他应用的文件Android 9 及以下READ/WRITE_EXTERNAL_STORAGE直接访问直接访问Android 10getExternalFilesDir()MediaStore受限,部分目录无法访问Android 11getExternalFilesDir()MediaStore受限,无法直接访问Android 13getExternalFilesDir()需要申请 READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO受限,无法直接访问