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

Android读取Pictures/TestApp目录图片无结果,求代码运行故障原因

核心问题原因

  1. 低版本系统字段不兼容
    MediaStore.MediaColumns.RELATIVE_PATH是Android 10(API 29)才新增的字段,API 28及以下的系统不存在这个字段,直接用该字段作为查询条件会导致查询失败,返回空结果。
  2. RELATIVE_PATH匹配规则错误
    MediaStore存储的RELATIVE_PATH默认末尾带有斜杠,你当前写的匹配规则是Pictures/TestApp%,实际系统存储的路径是Pictures/TestApp/,匹配规则不匹配导致查不到结果。
  3. 存储权限未正确申请
    不同Android版本要求的存储权限不同,如果未申请对应权限或者权限被拒绝,MediaStore查询会直接返回空游标。
  4. 低版本保存逻辑缺失目录创建步骤
    API 28及以下的保存逻辑中,你没有判断目标目录是否存在,直接创建文件输出流会导致保存失败,自然没有图片可查。
  5. MediaStore未及时刷新
    低版本系统直接写入本地文件后,MediaStore不会自动扫描更新,刚保存的图片不会立刻出现在查询结果中。

修复方案

第一步:确认权限配置

根据你适配的系统版本申请对应权限,所有权限需要动态申请通过后再执行查询操作:

  • API 33及以上:申请READ_MEDIA_IMAGES权限
  • API 29~32:申请READ_EXTERNAL_STORAGE权限
  • API 28及以下:申请READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限

第二步:修复保存逻辑

给低版本保存逻辑添加目录创建判断,高版本RELATIVE_PATH补全末尾斜杠:

private fun saveMediaToStorage(bitmap: Bitmap) {
    val filename = "${System.currentTimeMillis()}.jpg"
    var fos: OutputStream? = null
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        this.contentResolver?.also { resolver ->
            val contentValues = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
                put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
                // 给RELATIVE_PATH末尾加上斜杠,符合系统存储规范
                put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES+ File.separator + "TestApp" + File.separator)
                put(MediaStore.Images.Media.WIDTH, bitmap.width)
                put(MediaStore.Images.Media.HEIGHT, bitmap.height)
            }
            val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
            fos = imageUri?.let { resolver.openOutputStream(it) }
        }
    } else {
        val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES+ File.separator + "TestApp")
        // 新增目录创建判断
        if (!imagesDir.exists()) {
            imagesDir.mkdirs()
        }
        val image = File(imagesDir, filename)
        fos = FileOutputStream(image)
        // 低版本保存后通知MediaStore刷新
        sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(image)))
    }
    fos?.use {
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
        Toast.makeText(this, "saved the image", Toast.LENGTH_SHORT).show()
    }
}

第三步:修复查询逻辑,适配多版本

private suspend fun loadPhotosFromExternalStorage(): List<UserWork> {
    return withContext(Dispatchers.IO) {
        val collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        val projection = arrayOf(
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.DISPLAY_NAME
        )
        val photos = mutableListOf<UserWork>()
        // 分版本构造查询条件
        val selection: String
        val selectionArgs: Array<String>
        val targetDirPath = Environment.DIRECTORY_PICTURES + File.separator + "TestApp" + File.separator
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            selection = "${MediaStore.MediaColumns.RELATIVE_PATH} LIKE ?"
            selectionArgs = arrayOf("${targetDirPath}%")
        } else {
            // 低版本用DATA字段匹配绝对路径
            val targetAbsoluteDir = Environment.getExternalStoragePublicDirectory(targetDirPath).absolutePath
            selection = "${MediaStore.Images.Media.DATA} LIKE ?"
            selectionArgs = arrayOf("${targetAbsoluteDir}%")
        }
        requireActivity().contentResolver.query(
            collection,
            projection,
            selection,
            selectionArgs,
            "${MediaStore.Images.Media.DATE_ADDED} DESC" // 按添加时间排序比文件名排序更准确
        )?.use { cursor ->
            val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
            val displayNameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
            while (cursor.moveToNext()) {
                val id = cursor.getLong(idColumn)
                val displayName = cursor.getString(displayNameColumn)
                val contentUri = ContentUris.withAppendedId(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    id
                )
                photos.add(UserWork(id, displayName, contentUri))
            }
            photos.toList()
        } ?: listOf()
    }
}

内容的提问来源于stack exchange,提问作者sejigner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 10:51:03