Android 11使用Media Store API获取WhatsApp隐藏.Statuses文件夹文件
解决方案
方案1:修正MediaStore查询逻辑,直接读取.Statuses内容
MediaStore查不到.Statuses目录内容的核心原因是:Android 11(API 30)开始,MediaStore默认过滤以.开头的隐藏目录下的媒体文件,需要主动添加查询参数启用隐藏文件查询。该方案和你加载WhatsApp Images的逻辑完全一致,过滤、排序都在系统查询层完成,性能最优。
前置权限配置
- API 30~32:申请
READ_EXTERNAL_STORAGE权限 - API 33+:申请
READ_MEDIA_IMAGES、READ_MEDIA_VIDEO权限
代码示例
val projection = arrayOf( MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.MIME_TYPE, MediaStore.Files.FileColumns.DATE_MODIFIED, MediaStore.Files.FileColumns.RELATIVE_PATH ) // 匹配.Statuses目录路径 val selection = "${MediaStore.Files.FileColumns.RELATIVE_PATH} LIKE ?" val selectionArgs = arrayOf("%Android/media/com.whatsapp/WhatsApp/Media/.Statuses/%") val queryBundle = Bundle().apply { // 核心参数:允许查询隐藏目录下的文件 putInt(MediaStore.QUERY_ARG_INCLUDE_HIDDEN_FILES, 1) // 排除回收站文件 putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_EXCLUDE) // 系统层直接完成排序,无需后续自行处理 putString(MediaStore.QUERY_ARG_SORT_COLUMNS, MediaStore.Files.FileColumns.DATE_MODIFIED) putInt(MediaStore.QUERY_ARG_SORT_DIRECTION, MediaStore.QUERY_SORT_DIRECTION_DESCENDING) } // 用Files Uri可以同时查询图片和视频,无需分开请求 val contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL) context.contentResolver.query(contentUri, projection, selection, selectionArgs, null)?.use { cursor -> while (cursor.moveToNext()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)) // 直接生成可用的媒体Uri,不需要File对象 val mediaUri = ContentUris.withAppendedId(contentUri, id) val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)) val mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MIME_TYPE)) val modifyTime = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATE_MODIFIED)) // 此处处理你的业务逻辑 } }
方案2:基于已获取的SAF权限优化查询性能
如果要复用已经申请的Android/media目录SAF权限,不要使用DocumentFile.listFiles(),该方法会产生大量跨进程IPC调用导致性能极差,直接通过ContentResolver查询SAF Uri即可在系统层完成过滤、排序,性能比listFiles()高一个数量级。
代码示例
// 你已通过SAF获取的Android/media目录的Uri val androidMediaUri: Uri = 已获取的SAF权限Uri // 构造.Statuses目录的查询Uri val statusesUri = DocumentsContract.buildChildDocumentsUriUsingTree( androidMediaUri, DocumentsContract.getTreeDocumentId(androidMediaUri) + "/com.whatsapp/WhatsApp/Media/.Statuses" ) val projection = arrayOf( DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_MIME_TYPE, DocumentsContract.Document.COLUMN_LAST_MODIFIED ) // 系统层直接完成排序,查询效率接近原生MediaStore context.contentResolver.query( statusesUri, projection, null, null, "${DocumentsContract.Document.COLUMN_LAST_MODIFIED} DESC" // 查询时直接按修改时间倒序 )?.use { cursor -> while (cursor.moveToNext()) { val docId = cursor.getString(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DOCUMENT_ID)) val mediaUri = DocumentsContract.buildDocumentUriUsingTree(androidMediaUri, docId) val mimeType = cursor.getString(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_MIME_TYPE)) // 仅保留图片和视频 if (mimeType.startsWith("image/") || mimeType.startsWith("video/")) { // 此处处理你的业务逻辑 } } }
内容的提问来源于stack exchange,提问作者Nayab
相关产品推荐
相关产品推荐

