Android运行时权限异常:请求WRITE_EXTERNAL_STORAGE后应用设置无显示
核心原因
你的targetSdk设置为34(Android 14),而WRITE_EXTERNAL_STORAGE权限在Android 10(API29)开始就被逐步废弃,在Android 13(API33)及以上系统中,该权限不再被视为运行时权限——系统会直接忽略对它的申请请求,所以应用设置页面不会显示这个权限项。
同时,直接使用File类操作公共下载目录的方式,在Android 10+的分区存储机制下会受到限制,这也是你遇到「找不到该文件名」异常的根本原因。
解决方案
1. 移除无效的权限申请代码
删掉所有针对WRITE_EXTERNAL_STORAGE的运行时申请逻辑(包括requestRuntimePermissions方法和onRequestPermissionsResult的相关处理),这些代码在高版本系统中完全无效。
2. 调整Manifest权限声明
如果你的应用需要兼容Android 9(API28)及以下版本,可以给权限声明加上maxSdkVersion,避免高版本系统处理该权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
如果只需要兼容Android 10+,可以直接移除这个权限声明。
3. 使用MediaStore保存图片到下载文件夹
Android 10+推荐使用MediaStore API向公共下载目录写入文件,无需申请任何存储权限。以下是示例代码:
import android.content.ContentValues import android.graphics.Bitmap import android.os.Build import android.os.Environment import android.provider.MediaStore import android.widget.Toast import java.io.IOException private fun saveProcessedImageToDownloads(bitmap: Bitmap) { // 生成唯一文件名 val fileName = "removed_bg_${System.currentTimeMillis()}.png" val contentValues = ContentValues().apply { put(MediaStore.Downloads.DISPLAY_NAME, fileName) put(MediaStore.Downloads.MIME_TYPE, "image/png") // 指定保存到下载文件夹 put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 标记文件为待完成,避免系统提前扫描 put(MediaStore.Downloads.IS_PENDING, 1) } } contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)?.let { uri -> try { contentResolver.openOutputStream(uri)?.use { outputStream -> // 压缩并写入图片 bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 标记文件完成,系统会正常扫描到该文件 contentValues.put(MediaStore.Downloads.IS_PENDING, 0) contentResolver.update(uri, contentValues, null, null) } Toast.makeText(this, "图片已保存到下载文件夹", Toast.LENGTH_SHORT).show() } catch (e: IOException) { e.printStackTrace() Toast.makeText(this, "保存失败:${e.message}", Toast.LENGTH_SHORT).show() } } ?: run { Toast.makeText(this, "无法创建文件", Toast.LENGTH_SHORT).show() } }
4. 兼容Android 9及以下版本(可选)
如果需要兼容API28及以下系统,只需在保存文件前检查WRITE_EXTERNAL_STORAGE权限是否授予,授予后使用传统File方式保存即可,但这部分逻辑仅在低版本系统中触发。
验证
运行修改后的代码,处理完图片后调用saveProcessedImageToDownloads方法,图片会直接保存到系统下载文件夹,无需申请权限,也不会再出现「找不到文件名」的异常。
内容的提问来源于stack exchange,提问作者Rudranil Bhattacharjee

