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

Android 13下PhotoPicker无法访问全部文件夹的问题求助

Android 13 无法访问全部图片文件夹的解决方案

问题原因

Android 13(API 33)开始,系统默认的MediaStore.ACTION_PICK_IMAGES选择器仅展示媒体库已索引的文件夹(比如收藏、相机、截图、下载),未被媒体库扫描到的文件夹不会显示。同时READ_MEDIA_IMAGES权限仅授予访问媒体库中的媒体文件权限,并非全存储访问权限——这是Android 13针对隐私权限的收紧,和旧版本的权限逻辑不同。

另外你代码里在API33+时还申请了READ_EXTERNAL_STORAGE,这个权限在Android 13已经被废弃,无需申请,不会起到任何作用。

解决方案

根据需求,有两种可行方案:

方案1:使用系统文档树选择器(访问任意文件夹)

通过ACTION_OPEN_DOCUMENT_TREE让用户主动选择要访问的文件夹,获取该文件夹的访问权限后,即可遍历其中所有文件。代码示例:

private fun openDocumentTree() {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
    startActivityForResult(intent, REQUEST_CODE_DOCUMENT_TREE)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE_DOCUMENT_TREE && resultCode == RESULT_OK) {
        data?.data?.let { treeUri ->
            // 持久化权限,重启APP后仍有效
            contentResolver.takePersistableUriPermission(
                treeUri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION
            )
            // 遍历文件夹内的文件,需要使用DocumentFile操作
            val rootDocument = DocumentFile.fromTreeUri(this, treeUri)
            rootDocument?.listFiles()?.forEach { file ->
                // 处理文件,比如判断是否为图片
                if (file.type?.startsWith("image/") == true) {
                    // 执行你的逻辑
                }
            }
        }
    }
}

注意:这种方式需要用户手动选择文件夹,符合Android隐私规范。

方案2:自定义媒体选择器(遍历所有媒体文件)

如果你的需求是访问所有图片文件(不管所在文件夹),可以直接查询MediaStore获取所有图片的Uri,然后自己构建选择界面,绕过系统默认选择器的限制。代码示例:

private fun queryAllImages() {
    val projection = arrayOf(
        MediaStore.Images.Media._ID,
        MediaStore.Images.Media.DISPLAY_NAME,
        MediaStore.Images.Media.BUCKET_DISPLAY_NAME // 获取文件夹名称
    )

    val cursor = contentResolver.query(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        projection,
        null,
        null,
        "${MediaStore.Images.Media.DATE_ADDED} DESC"
    )

    cursor?.use {
        val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
        val folderColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME)

        val folderMap = mutableMapOf<String, MutableList<Uri>>()

        while (it.moveToNext()) {
            val id = it.getLong(idColumn)
            val folderName = it.getString(folderColumn)
            val imageUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
            
            if (!folderMap.containsKey(folderName)) {
                folderMap[folderName] = mutableListOf()
            }
            folderMap[folderName]?.add(imageUri)
        }

        // folderMap中存储了所有文件夹及其对应的图片Uri,可用于构建自定义选择界面
    }
}

这种方式可以获取到媒体库中所有已索引的图片,按文件夹分类展示,不受系统选择器的限制。

权限调整

修改你的checkPermission方法,API33+时移除无用的READ_EXTERNAL_STORAGE:

private fun checkPermission() {
    val PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        arrayOf(Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.POST_NOTIFICATIONS)
    } else {
        arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
    }
    TedPermission(this)
        .setPermissionListener(permissionlistener)
        .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
        .setPermissions(*PERMISSIONS)
        .check()
}

内容的提问来源于stack exchange,提问作者tej shah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 01:01:30