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

Android 14设备READ_EXTERNAL_STORAGE权限弹窗不显示问题求助

问题分析与解决方案

一、权限弹窗不显示的核心原因与修复

Android 14(API 34)对存储权限体系做了重大调整:READ_EXTERNAL_STORAGE权限在API 33+已被废弃,系统不再处理该权限的请求弹窗,直接返回"未授予"状态,这就是你遇到弹窗不显示、直接触发Toast的根本原因。

修复步骤:

  1. 更新清单权限声明
    替换原有权限,根据业务场景添加对应媒体权限,同时保留旧权限兼容低版本:

    <!-- 兼容 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" /> -->
    
  2. 修改权限请求代码
    按系统版本区分权限检查与请求逻辑:

    // 初始化权限请求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文件夹下的非相机拍摄内容),这些内容属于其他应用的媒体集合,需通过以下方式访问:

解决方案:

  1. 通过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();
    }
    
  2. 使用存储访问框架(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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 21:35:17