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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 15:06:05