为啥 ContentResolver 看不到其他应用添加的文件?
Posted
技术标签:
【中文标题】为啥 ContentResolver 看不到其他应用添加的文件?【英文标题】:Why ContentResolver does not see added files by another app?为什么 ContentResolver 看不到其他应用添加的文件? 【发布时间】:2020-10-09 08:53:51 【问题描述】:我使用ContentResolver.insert
将文件添加到Documents/MyExcelsFolder
,然后还通过另一个应用程序将新文件添加到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 级别 29)
目标 API 版本:api 29
【问题讨论】:
【参考方案1】:从 Android 10 开始,有一个新的存储访问模型正在运行,称为 Scoped Storage,它的限制性更强。简而言之:
-
您的应用始终可以访问自己的目录。
您的应用可以写入(在
ContentResolver.insert
的帮助下)the shared media 集合,并且只能读取您的应用创建的文件。您可以通过请求READ_EXTERNAL_STORAGE
权限来访问这些集合中的其他应用文件。
您的应用可以使用file or directory system pickers 访问其他文件和目录。
这有点奇怪,看起来像一个错误,您可以通过MediaStore.Files
集合访问xls
文件。 The documentation 说
媒体商店还包括一个名为
MediaStore.Files
的集合。 它的内容取决于您的应用是否使用范围存储,可用 在面向 Android 10 或更高版本的应用上:如果启用了范围存储,则集合仅显示照片, 您的应用创建的视频和音频文件。
如果是范围存储 不可用或未被使用,该集合显示所有类型的 媒体文件。
但无论如何,您仍然无法访问上述其他应用程序创建的文件。 因此,根据您的用例,有几个选项可供选择:
-
由于现在可以通过
MediaStore.Files
访问文件,您可以尝试请求READ_EXTERNAL_STORAGE
权限,如this table 所示,以获得对媒体收藏的非过滤访问。但我希望这种方式在不同的设备上运行不可靠和/或希望停止使用新的更新,因为媒体集合应该只用于媒体文件。
您可以使用ACTION_OPEN_DOCUMENT
或ACTION_OPEN_DOCUMENT_TREE
向用户显示文件/目录选择器并访问文件或整个目录树。也请检查此方法的the restrictions。我会说这是最好的方法。
Android 10 允许您使用android:requestLegacyExternalStorage
标志从范围存储中temporarily opt-out。但 Android 11 已经发布,这个标志在其中没有任何作用。
您可以请求新的MANAGE_EXTERNAL_STORAGE
权限,然后向access all files 请求用户的特殊白名单。这就是文件管理器现在的工作方式。此功能从 Android 11 开始可用,因此您可能会为 Android 10 使用选择退出标志。如果您要在那里发布您的应用,请务必检查使用此功能的限制和 Google Play 政策。
【讨论】:
以上是关于为啥 ContentResolver 看不到其他应用添加的文件?的主要内容,如果未能解决你的问题,请参考以下文章
Android开发实践 ContentProvider和ContentResolver
ContentProvider与ContentResolver使用
Android MediaStore:Images.Media.insertImage 与 ContentResolver.insert
内容提供者ContentProvider和内容解析者ContentResolver