Android 14设备READ_EXTERNAL_STORAGE权限弹窗不显示问题求助
问题分析与解决方案
一、权限弹窗不显示的核心原因与修复
Android 14(API 34)对存储权限体系做了重大调整:READ_EXTERNAL_STORAGE权限在API 33+已被废弃,系统不再处理该权限的请求弹窗,直接返回"未授予"状态,这就是你遇到弹窗不显示、直接触发Toast的根本原因。
修复步骤:
更新清单权限声明
替换原有权限,根据业务场景添加对应媒体权限,同时保留旧权限兼容低版本:<!-- 兼容 API < 33 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- API 33+ 读取图片权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 如需读取视频/音频,添加对应权限 --> <!-- <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> --> <!-- <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> -->修改权限请求代码
按系统版本区分权限检查与请求逻辑:// 初始化权限请求Launcher ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { if (!isGranted) { Toasty.error(context, "Can't continue without the required permissions", Toast.LENGTH_LONG).show(); } }); // 按钮点击时的权限检查逻辑 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) { requestPermissionLauncher.launch(Manifest.permission.READ_MEDIA_IMAGES); return; } } else { if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE); return; } }
二、手动授权后无法读取非DCIM文件夹的解决
Android 14严格遵循分区存储规则:即使授予媒体权限,应用也无法直接访问其他应用的私有媒体目录(如Facebook、WhatsApp、Download文件夹下的非相机拍摄内容),这些内容属于其他应用的媒体集合,需通过以下方式访问:
解决方案:
通过MediaStore查询媒体文件
所有可公开访问的媒体文件都会被MediaStore索引,通过ContentResolver查询获取:// 查询所有图片示例 String[] projection = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, MediaStore.Images.Media.DATE_ADDED + " DESC" ); if (cursor != null) { while (cursor.moveToNext()) { String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)); String filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)); // 处理文件逻辑... } cursor.close(); }使用存储访问框架(SAF)获取文件夹授权
若需访问特定文件夹(如Download、WhatsApp文件夹),可通过SAF请求用户主动授权:private static final int REQUEST_CODE_OPEN_DOCUMENT_TREE = 1001; // 启动SAF选择文件夹 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE); // 处理授权结果 @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_OPEN_DOCUMENT_TREE && resultCode == RESULT_OK) { if (data != null) { Uri treeUri = data.getData(); // 持久化权限(可选,避免重启后失效) getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); // 通过DocumentFile访问文件夹内容 DocumentFile documentFile = DocumentFile.fromTreeUri(this, treeUri); // 遍历文件逻辑... } } }
关键注意事项
- 分区存储是Android 10+的强制规则,targetSdkVersion=34时必须适配,不能再依赖
READ_EXTERNAL_STORAGE的全存储访问能力。 - 测试前请彻底卸载旧版本应用,清除残留的权限状态缓存。
内容的提问来源于stack exchange,提问作者mizel hozal
相关产品推荐
相关产品推荐

