【问题标题】:createChooser() intent exception "requires the provider be exported, or grantUriPermission()"createChooser() 意向异常\"需要导出提供者,或 grantUriPermission()\"
【发布时间】:2023-01-05 16:03:29
【问题描述】:

该应用程序需要与其他应用程序共享存储在 cacheDir 根目录中的 PDF 文件。该问题出现在 Android 12 上,也可能出现在其他版本上。

显现:

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

供应商路径:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <cache-path name="cache" path="." />
</paths>

意图:

        val pdfFile = File(requireContext().cacheDir, pdfFileName)
        val fileUri: Uri = FileProvider.getUriForFile(
            requireContext().applicationContext,
            requireContext().packageName.toString() + ".provider",
            pdfFile
        )
        val intent = Intent()
        intent.action = Intent.ACTION_SEND
        intent.putExtra(Intent.EXTRA_STREAM, fileUri)
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.type = "application/pdf"
        startActivity(Intent.createChooser(intent, "Share Document"))

共享表成功打开,但此异常始终显示在该点,随后共享到另一个应用程序失败。

Writing exception to parcel
    java.lang.SecurityException: Permission Denial: reading 
androidx.core.content.FileProvider uri
content://uk.co.packagename.provider/cache/8BEDF7212-0DE46-42B0-9FA9-32C434BDD2F3HO.pdf
from pid=15363, uid=1000 requires the provider be exported, or grantUriPermission()

提供程序作为一个整体无法导出,并且 URI 权限似乎已被授予。我已经通读了 Android 文件共享文档和许多 S/O 答案,但我看不出有什么需要更正的,你能吗?

【问题讨论】:

  • 看起来不错。你试过ShareCompat.IntentBuilder.from(this).setType("application/pdf").addStream(fileUri).startChooser()吗?
  • @CommonsWare 谢谢,ShareCompat 不会触发权限错误(yay)......但共享随后仍然失败(boo),例如共享到 Gmail 会导致:ComposeActivity:添加附件时出错 [CONTEXT android_log_tag="ComposeActivity"] fha:openAssetFileDescriptor 时出现 FileNotFoundException。
  • 尝试将您权限中的 requireContext().packageName.toString() 替换为 BuildConfig.APPLICATION_ID。除此之外,你是否当然pdfFile 存在吗?
  • @CommonsWare Mark,因为您怀疑该文件不存在,只有一个具有相似名称的文件。非常感谢你提示我确认。如果您想添加一个答案以建议遇到此问题的任何人可能想要仔细检查该文件,我很乐意将其标记为答案(至少对我而言)。

标签: android android-contentprovider scoped-storage


【解决方案1】:

FileProvider.getUriForFile() 的限制之一是它不检查文件是否存在。将 Uri 获取到不存在的文件是有正当理由的,例如 ACTION_IMAGE_CAPTURE。不过,这意味着仅仅获得 Uri 并不能保证 Uri 对阅读内容有用。

使这个问题更加复杂的是,通过exists() 的“文件是否存在”感觉可能有点冒险,尤其是对于外部存储。

因此,在调用 getUriForFile() 之前,确保您拥有正确的 File 对象非常重要,并且它应该指向一个已经存在的文件。

【讨论】:

    【解决方案2】:

    查了一下是什么包导致了这个异常,原来是android系统。所以在授予 uri 权限后,我在日志中看不到更多这样的异常:

    requireContext().grantUriPermission("android", fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
    

    【讨论】:

      【解决方案3】:

      安全异常的原因(“要求提供者被导出,或 grantUriPermission()”) 被抛出是因为 android 系统正在尝试访问共享资源,以便为用户提供共享内容的预览。

      最好的方法是使用intent.setClipData分享资源除了intent.putExtra(Intent.EXTRA_STREAM, fileUri)

      代码应如下所示:

      intent.action = Intent.ACTION_SEND
      intent.setClipData(ClipData.newRawUri("", fileUri))
      intent.putExtra(Intent.EXTRA_STREAM, fileUri)
      intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
      

      @skvalex上面的答案有效,但这是一种让 Android 系统访问文件以在应用程序共享选择器屏幕中进行预览的 hacky 方式。

      有关这方面的其他参考,请同时查看 FileProvider 类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-06-19
        • 2023-01-02
        • 2016-02-05
        • 2020-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多