为什么ContentResolver看不到其他应用程序添加的文件



我使用ContentResolver.insert将文件添加到Documents/MyExcelsFolder,然后还通过另一个应用程序(例如FileManager(将新文件添加到了Documents/MyExcelsFolder文件夹

然后我尝试从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
)
}

结果是insert命令只添加了来自ContentResolver的文件,而FileManager没有复制任何文件。如何查看cursor中的所有文件?

操作系统:Android 10(Q((API 29级(

目标API版本:API 29

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

  1. 您的应用程序始终可以访问自己的目录
  2. 您的应用程序可以(在ContentResolver.insert的帮助下(写入共享媒体收藏集,并且可以从中只读应用程序创建的文件。您可以通过请求READ_EXTERNAL_STORAGE权限来访问这些集合中的其他应用程序文件
  3. 您的应用程序可以使用文件或目录系统选择器访问其他文件和目录

您可以通过MediaStore.Files集合访问xls文件,这有点奇怪,看起来像是一个bug。文件显示

媒体存储还包括一个名为MediaStore.Files的集合。其内容取决于您的应用程序是否使用作用域存储针对Android 10或更高版本的应用程序:

如果启用了作用域存储,则集合仅显示照片,视频和音频文件。

如果作用域存储不可用或未被使用,集合显示所有类型的媒体文件。

但无论如何,您仍然无法访问如上所述的其他应用程序创建的文件。因此,根据您的用例,有几个选项可供选择:

  1. 由于现在可以通过MediaStore.Files访问文件,您可以尝试请求READ_EXTERNAL_STORAGE权限,如本表所示,以获得对媒体收藏集的非过滤访问权限。但我希望这种方式在不同的设备上不可靠地工作,和/或希望在新的更新中停止工作,因为媒体收藏应该只用于媒体文件
  2. 您可以使用ACTION_OPEN_DOCUMENTACTION_OPEN_DOCUMENT_TREE向用户显示文件/目录选择器,并访问文件或整个目录树。请同时检查此方法的限制。我认为这是最好的方式
  3. 安卓10允许您使用android:requestLegacyExternalStorage标志暂时退出作用域存储。但安卓11已经发布了,这个标志在其中没有任何效果
  4. 您可以请求新的MANAGE_EXTERNAL_STORAGE权限,然后由用户请求一个特殊的白名单来访问所有文件。这就是文件管理器现在的工作方式。此功能从Android 11开始提供,因此您可能会在Android 10中使用选择退出标志。如果你打算在那里发布你的应用程序,也一定要检查使用此功能的限制和Google Play政策

相关内容

最新更新