android - 为什么 ContentResolver 看不到其他应用添加的文件?

标签 android file uri android-contentresolver file-manager

我将文件添加到 Documents/MyExcelsFolder通过使用 ContentResolver.insert然后还将新文件添加到 Documents/MyExcelsFolder另一个应用程序的文件夹(例如 FileManager )
然后我尝试从 MyExcelsFolder 获取所有文件文件夹

fun getAppFiles(context: Context): List<AppFile> {
        val appFiles = mutableListOf<AppFile>()

        val contentResolver = context.contentResolver
        val columns = mutableListOf(
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.DATA,
            MediaStore.Images.Media.DATE_ADDED,
            MediaStore.Images.Media.DISPLAY_NAME,
            MediaStore.Images.Media.MIME_TYPE
        ).apply {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                add(
                    MediaStore.MediaColumns.RELATIVE_PATH
                )
            }
        }.toTypedArray()
        val extensions = listOf("xls", "xlsx")
        val mimes = extensions.map { MimeTypeMap.getSingleton().getMimeTypeFromExtension(it) }

        val selection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            "${MediaStore.MediaColumns.RELATIVE_PATH} LIKE ?"
        } else {
            "${MediaStore.Images.Media.DATA} LIKE ?"
        }

        val selectionArgs = arrayOf(
            "%${Environment.DIRECTORY_DOCUMENTS}/MyExcelsFolder%"
        )

        contentResolver.query(
            MediaStore.Files.getContentUri("external"),
            columns,
            selection,
            selectionArgs,
            MediaStore.Files.FileColumns.DATE_ADDED + " DESC"
        )?.use { cursor ->
            while (cursor.moveToNext()) {
                val pathColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
                val mimeColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE)
                val filePath = cursor.getString(pathColumnIndex)
                val mimeType = cursor.getString(mimeColumnIndex)
                if (mimeType != null && mimes.contains(mimeType)) {
                    // handle cursor
                    appFiles.add(cursor.toAppFile())
                } else {
                    // need to check extension, because the Mime Type is null
                    val extension = File(filePath).extension
                    if (extensions.contains(extension)) {
                        // handle cursor
                        appFiles.add(cursor.toAppFile())
                    }
                }
            }
        }

        return appFiles
    }

fun Cursor.toAppFile(): AppFile {
    val cursor = this

    val idColumnIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)
    val nameColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
    val mimeColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE)
    val pathColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA)

    val id = cursor.getLong(idColumnIndex)
    val uri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id)
    val fileDisplayName = cursor.getString(nameColumnIndex)
    val filePath = cursor.getString(pathColumnIndex)
    var mimeType = cursor.getString(mimeColumnIndex)
    val relativePath = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.RELATIVE_PATH))
    } else {
        null
    }
    var type = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
    if (type == null) {
        type = File(filePath).extension
        mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(type)
    }
    return AppFile(
        id = id,
        uri = uri,
        absolutePath = filePath,
        name = fileDisplayName,
        mimeType = mimeType,
        extension = type,
        relativePath = relativePath
    )
}
结果只有 ContentResolver 中的文件添加者 insert命令,并且文件管理器没有复制任何文件。如何查看 cursor 中的所有文件?
操作系统:Android 10 (Q) (API level 29)
目标 API 版本:api 29

最佳答案

从 Android 10 开始,有一个新的存储访问模型正在运行,称为 Scoped Storage而且限制性更强。简而言之:

  • 您的应用始终可以访问自己的目录。
  • 您的应用程序可以写入(在 ContentResolver.insert 的帮助下)到 the shared media收藏和只能读取您的应用程序创建的文件 从他们。您可以通过请求 READ_EXTERNAL_STORAGE 访问这些集合中的其他应用程序文件。允许。
  • 您的应用程序可以使用 file or directory system pickers 访问其他文件和目录.

  • 这有点奇怪,看起来像一个你可以访问的错误 xls文件通过 MediaStore.Files收藏。 The documentation

    The media store also includes a collection called MediaStore.Files. Its contents depend on whether your app uses scoped storage, available on apps that target Android 10 or higher:

    If scoped storage is enabled, the collection shows only the photos, videos, and audio files that your app has created.

    If scoped storage is unavailable or not being used, the collection shows all types of media files.


    但无论如何,您仍然无法访问上述其他应用程序创建的文件。
    因此,根据您的用例,有几个选项可供选择:
  • 通过 MediaStore.Files 访问文件现在为你工作,你可以尝试请求READ_EXTERNAL_STORAGE权限如this table获得对媒体收藏的非过滤访问。但我希望这种方式在不同的设备上不可靠地工作和/或希望停止它与新的更新一起工作,因为媒体集合应该只用于媒体文件。
  • 您可以使用 ACTION_OPEN_DOCUMENTACTION_OPEN_DOCUMENT_TREE向用户显示文件/目录选择器并访问文件或整个目录树。请查看the restrictions此法之也。我会说这是最好的方法。
  • Android 10 允许您 temporarily opt-out使用 android:requestLegacyExternalStorage 从作用域存储旗帜。但是 Android 11 已经发布了,这个标志在其中没有任何作用。
  • 您可以申请新的MANAGE_EXTERNAL_STORAGE许可,然后由用户向 access all files 请求特殊的白名单.这就是文件管理器现在的工作方式。此功能从 Android 11 开始可用,因此您可能会为 Android 10 使用选择退出标志。如果您要在那里发布您的应用,请务必检查使用此功能的限制和 Google Play 政策。
  • 关于android - 为什么 ContentResolver 看不到其他应用添加的文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64276816/

    相关文章:

    android - Jetpack Compose 中未显示 Snackbar

    java - 约束布局是否应该包裹所有其他 View ?

    linux - 查找文件名中数字大于的文件

    window - 如何在 Chrome 应用程序中显示 PDF 的数据 URI?

    android - WebView 不加载 Instagram 登录页面

    java - SAX XML解析添加参数

    c# - 如何以编程方式获取文件夹结构下的锁定文件列表?

    python - 从同时包含印地语和英语的文件中仅提取印地语文本

    c# - 替换 Uri 中的主机

    java - 如何删除 uri 路径信息?