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

Android 13+应用图片分享权限错误及合规实现方案咨询

Android 13+ 分享Drawable图片的正确实现方案

问题背景

完全不了解应用间内容分享机制,应用内需分享的图片初始存储在drawable目录。当前实现是将图片写入外部文件,通过FileProvider分享,但持续遭遇权限错误:

java.io.FileNotFoundException: /storage/emulated/0/Documents/image.png: open failed: EACCES (Permission denied)
或
java.io.FileNotFoundException: /storage/emulated/0/image.png: open failed: EPERM (Operation not permitted)

Android 13+中申请读写外部存储权限已无效,申请MANAGE_EXTERNAL_STORAGE权限则可能导致应用无法上架Google Play,需明确正确实现方式。

当前实现代码:

val varName = "p_$muhImage"
val resourceId: Int =context.getResources().getIdentifier(varName, "drawable", context.packageName)

val bitmap =BitmapFactory.decodeResource(context.resources, resourceId)
val path= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString() + "/image.png"
try {
    FileOutputStream(path).use { out ->
        bitmap.compress(
            Bitmap.CompressFormat.PNG,
            100,
            out
        )
    }
} catch (e: IOException) {
    e.printStackTrace()
}
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
shareIntent.type = "image/*"
val imageUri = FileProvider.getUriForFile(
    context,
    context.getPackageName() + ".fileprovider",
    File(path)
)
shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri)

shareIntent.putExtra(Intent.EXTRA_TITLE, "share")
shareIntent.putExtra(Intent.EXTRA_TEXT, "share")

val chooserIntent = Intent.createChooser(shareIntent, "share")
chooserIntent.putExtra(Intent.EXTRA_TITLE, "share")
chooserIntent.putExtra(Intent.EXTRA_TEXT, "share")
chooserIntent.putExtra(Intent.EXTRA_CONTENT_QUERY, "image/*")

context.startActivity(
    Intent.createChooser(
        shareIntent,
        "share too"
    )
);

File(path).deleteOnExit();

Manifest权限配置:

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

Manifest中Provider配置:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider"/>
</provider>

Provider XML配置:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

正确实现方案

核心逻辑

避开外部公共目录的权限限制,改用应用私有缓存目录存储待分享图片,配合FileProvider完成跨应用授权分享,全程无需任何外部存储权限。

步骤1:修改FileProvider路径配置

更新res/xml/provider.xml,指向应用私有缓存目录:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 配置为应用私有缓存目录 -->
    <cache-path name="cache_files" path="."/>
</paths>

步骤2:重构分享代码

将图片写入应用私有缓存目录,无需权限即可读写:

val varName = "p_$muhImage"
val resourceId: Int = context.resources.getIdentifier(varName, "drawable", context.packageName)
val bitmap = BitmapFactory.decodeResource(context.resources, resourceId)

// 改用应用私有缓存目录,无需权限
val cacheDir = context.cacheDir
val shareFile = File(cacheDir, "share_image.png")

try {
    FileOutputStream(shareFile).use { out ->
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
    }
} catch (e: IOException) {
    e.printStackTrace()
    return // 处理写入失败场景
}

val shareIntent = Intent(Intent.ACTION_SEND)
// 必须添加该Flag,授予接收方临时读取权限
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
shareIntent.type = "image/*"

val imageUri = FileProvider.getUriForFile(
    context,
    "${context.packageName}.fileprovider",
    shareFile
)
shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri)
shareIntent.putExtra(Intent.EXTRA_TITLE, "分享图片")
shareIntent.putExtra(Intent.EXTRA_TEXT, "这是我分享的图片")

// 启动分享选择器
context.startActivity(Intent.createChooser(shareIntent, "选择分享方式"))

// 延迟删除文件,避免分享时文件被提前清理
shareFile.deleteOnExit()

步骤3:清理无用权限

删除Manifest中无需再使用的外部存储权限:

<!-- 移除以下权限 -->
<!-- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> -->
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> -->

关键原理说明

  1. 应用私有目录:context.cacheDir是应用专属的私有缓存目录,仅当前应用可读写,Android全版本无权限限制。
  2. FileProvider:将私有文件路径转换为Content Uri,通过临时授权机制让目标应用安全访问文件,避免直接暴露文件路径的安全风险。
  3. FLAG_GRANT_READ_URI_PERMISSION:必须添加该Flag,否则接收方应用无法读取Uri指向的文件,会触发权限错误。

内容的提问来源于stack exchange,提问作者M.KH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 22:28:09