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

Android通过ContentProvider传递图片URI遇权限异常求解决方案

问题

我希望通过ContentProvider以Cursor行数据形式向其他App传递图片文件,在自定义ContentProvider的query方法中生成FileProvider的图片Content URI并存入Cursor返回,相关代码如下:

override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {
    val cursor = MatrixCursor(arrayOf("id", "title", "image"))
    val movie = getMovie()
    val rowBuilder = cursor.newRow()
    rowBuilder.add("id", movie.id)
    rowBuilder.add("title", movie.title)

    val fileUri = FileProvider.getUriForFile(context!!, "com.my.content.provider.fileProvider", movie.imageFile)
    rowBuilder.add("image", fileUri.toString())

    return cursor
}

FileProvider的配置如下:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.my.content.provider.fileProvider"
    android:enabled="true"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

但其他App查询该ContentProvider获取到图片URI后,调用contentResolver.openInputStream访问时触发SecurityException:Permission Denial: opening provider androidx.core.content.FileProvider that is not exported from UID 10437。其他App的调用代码如下:

val cursor: Cursor? = contentResolver.query(
            Uri.parse("content://com.my.content.provider/movies/1"),
            null,
            null,
            null,
            null
        )
val image = cursor.getString(cursor.getColumnIndexOrThrow("image"))  // 获取图片URI成功

val inStream = contentResolver.openInputStream(Uri.parse(image))  // 抛出异常

val bitmap = BitmapFactory.decodeStream(inStream)

请问如何为该URI授予读取权限?目前仅找到通过Intent共享Content URI的示例,是否可以通过ContentProvider传递此类URI?


解决方案

1. 在自定义ContentProvider中手动授予URI权限

你的FileProvider已经配置了grantUriPermissions="true",可以在query方法中直接给请求方App授予读取权限。修改后的query方法如下:

override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {
    val cursor = MatrixCursor(arrayOf("id", "title", "image"))
    val movie = getMovie()
    val rowBuilder = cursor.newRow()
    rowBuilder.add("id", movie.id)
    rowBuilder.add("title", movie.title)

    val fileUri = FileProvider.getUriForFile(context!!, "com.my.content.provider.fileProvider", movie.imageFile)
    rowBuilder.add("image", fileUri.toString())

    // 获取请求方包名,授予读取权限
    val callingPackage = context.callingPackage
    callingPackage?.let {
        context.grantUriPermission(
            it,
            fileUri,
            Intent.FLAG_GRANT_READ_URI_PERMISSION
        )
    }

    return cursor
}

2. 可选:配置持久化权限(如需长期访问)

如果需要让权限在Cursor关闭后仍能生效,可添加持久化权限标记。在自定义ContentProvider的权限授予代码中修改:

context.grantUriPermission(
    callingPackage,
    fileUri,
    Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
)

然后在调用方App中,获取URI后调用:

contentResolver.takePersistableUriPermission(
    Uri.parse(image),
    Intent.FLAG_GRANT_READ_URI_PERMISSION
)

问题根源说明

FileProvider默认exported="false",不会对外开放访问权限。通过Intent共享URI时,系统会自动处理权限授予,但直接通过Cursor传递URI时,系统不会自动授权,必须手动调用grantUriPermission给请求方App赋予对应权限。

ContentProvider传递URI的可行性

完全可以通过ContentProvider传递FileProvider的URI,核心是确保请求方App拥有访问该URI的权限,手动完成权限授予即可实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 18:06:29