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

全文件访问权限合规问询:如何改用Media Store API替代MANAGE_EXTERNAL_STORAGE

解决MANAGE_EXTERNAL_STORAGE权限合规问题:改用Media Store API指南

一、移除违规权限声明

首先修改AndroidManifest.xml,删除以下权限声明:

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

根据应用核心功能需求,保留必要的媒体权限:

  • Android 13(API 33)及以上:按需添加READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO
  • Android 10-12(API 29-32):添加READ_EXTERNAL_STORAGE(读取媒体用),写入公共媒体目录无需WRITE_EXTERNAL_STORAGE
  • Android 9及以下:需添加READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE

二、用Media Store API实现媒体文件读取

以读取图片为例,通过ContentResolver查询公共图片库:

fun loadImages(contentResolver: ContentResolver): List<MediaItem> {
    val projection = arrayOf(
        MediaStore.Images.Media._ID,
        MediaStore.Images.Media.DISPLAY_NAME,
        MediaStore.Images.Media.DATE_ADDED
    )
    val sortOrder = "${MediaStore.Images.Media.DATE_ADDED} DESC"
    val images = mutableListOf<MediaItem>()

    contentResolver.query(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        projection,
        null,
        null,
        sortOrder
    )?.use { cursor ->
        val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
        val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
        
        while (cursor.moveToNext()) {
            val id = cursor.getLong(idColumn)
            val name = cursor.getString(nameColumn)
            val contentUri = ContentUris.withAppendedId(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                id
            )
            images.add(MediaItem(id, name, contentUri))
        }
    }
    return images
}

data class MediaItem(val id: Long, val name: String, val contentUri: Uri)

读取视频、音频只需替换对应的MediaStore路径(比如MediaStore.Video.Media.EXTERNAL_CONTENT_URI)。

三、用Media Store API实现媒体文件写入

以保存Bitmap到公共图片目录为例:

fun saveBitmapToGallery(
    contentResolver: ContentResolver,
    bitmap: Bitmap,
    fileName: String
): Uri? {
    val contentValues = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
        put(MediaStore.Images.Media.MIME_TYPE, "image/png")
        // Android 10+支持指定相对路径,分类保存到应用专属子目录
        put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/MyApp")
        put(MediaStore.Images.Media.IS_PENDING, 1)
    }

    val uri = contentResolver.insert(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        contentValues
    ) ?: return null

    uri.let {
        contentResolver.openOutputStream(it)?.use { outputStream ->
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
        }
        // 标记文件为已完成,让系统媒体扫描器识别
        contentValues.put(MediaStore.Images.Media.IS_PENDING, 0)
        contentResolver.update(it, contentValues, null, null)
    }
    return uri
}

写入视频、音频的逻辑类似,替换MIME类型和MediaStore路径即可。

四、权限请求适配

  • Android 13+:动态请求对应的媒体权限(比如READ_MEDIA_IMAGES),用ActivityResultContracts.RequestPermission或RequestMultiplePermissions
  • Android 10-12:动态请求READ_EXTERNAL_STORAGE权限
  • Android 9及以下:动态请求READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限

五、注意事项

  • 避免直接访问文件路径(比如Environment.getExternalStorageDirectory()),改用Content URI操作媒体文件
  • 若需访问下载目录的非媒体文件,可使用ACTION_OPEN_DOCUMENT让用户手动选择,而非申请全文件权限
  • 测试时覆盖不同Android版本,确保所有媒体读写功能正常运行

内容的提问来源于stack exchange,提问作者VU Virtual Universe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 03:50:27