【问题标题】:FileProvider - java.lang.IllegalArgumentException: Failed to find configured root that containsFileProvider - java.lang.IllegalArgumentException:未能找到包含的已配置根
【发布时间】:2020-06-22 21:41:24
【问题描述】:

我正在尝试使用 FileProvider 公开 URI,但一直遇到此异常:

java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.example.android.development/cache/images/background.png

AndroidManifest.xml:

<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/provider_paths"/>
</provider>

provider_paths.xml:

<paths>
    <cache-path name="images" path="images/"/>
</paths>

保存文件:

fun cache(context: Context, bitmap: Bitmap, fileName: String) {
    val filePath = File(context.cacheDir, "images")
    val file = File(filePath, fileName)
    try {
        filePath.mkdirs()
        val fos = FileOutputStream(file)
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
        fos.close()
    } catch (e: IOException) {
        throw RuntimeException(e)
    }
}

获取 URI:

fun getUri(context: Context, fileName: String): Uri {
    val filePath = File(context.cacheDir, "images")
    val file = File(filePath, fileName)
    return FileProvider.getUriForFile(context, "${context.applicationContext.packageName}.provider", file)
}

我做了一些调试,似乎FileProvidergetUriForFile(File file)函数首先将文件路径/data/user/0/com.example.android.development/cache/images/background.png转换为规范文件路径/data/data/com.example.android.development/cache/images/background.png,然后尝试将根路径/storage/emulated/0与规范文件匹配小路。当它没有找到这个匹配时,它会抛出异常。 我不确定我做错了什么导致此问题或如何解决此问题?

调试 FileProvider.java getUriForFile(File file):

@Override
public Uri getUriForFile(File file) { // file: "/data/user/0/com.example.android.development/cache/images/background.png"
    String path; // path: "/data/data/com.example.android.development/cache/images/background.png"
    try {
        path = file.getCanonicalPath();
    } catch (IOException e) {
        throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
    }

    // Find the most-specific root path
    Map.Entry<String, File> mostSpecific = null;
    for (Map.Entry<String, File> root : mRoots.entrySet()) {
        final String rootPath = root.getValue().getPath(); // rootPath: "/storage/emulated/0"
        if (path.startsWith(rootPath) && (mostSpecific == null // path: "/data/data/com.example.android.development/cache/images/background.png"
                || rootPath.length() > mostSpecific.getValue().getPath().length())) {
            mostSpecific = root; // mostSpecific: null
        }
    }

    if (mostSpecific == null) {
        throw new IllegalArgumentException(
                "Failed to find configured root that contains " + path);
    }

    // Start at first char of path under root
    final String rootPath = mostSpecific.getValue().getPath();
    if (rootPath.endsWith("/")) {
        path = path.substring(rootPath.length());
    } else {
        path = path.substring(rootPath.length() + 1);
    }

    // Encode the tag and path separately
    path = Uri.encode(mostSpecific.getKey()) + '/' + Uri.encode(path, "/");
    return new Uri.Builder().scheme("content")
            .authority(mAuthority).encodedPath(path).build();
}

我已经尝试过的事情:

  1. &lt;root-path name="root" path="." /&gt; 添加到&lt;paths&gt;
  2. 尝试filesDir 而不是cacheDir
  3. 在手机而不是模拟器上运行应用程序

【问题讨论】:

  • “然后尝试将根路径 /storage/emulated/0 与规范文件路径匹配”——这与您的 provider_paths.xml 不匹配。将"${context.applicationContext.packageName}.provider" 更改为"${BuildConfig.APPLICATION_ID}.provider",看看是否会得到不同的结果。
  • 不走运,我得到了同样的结果。 context.applicationContext.packageNameBuildConfig.APPLICATION_ID 都返回 com.example.android.development
  • 进入 Android Studio 中的 Build > APK Analyzer 并分析您的 APK。在您获得的选项卡中,进入 res/xml/provider_paths.xml 并查看它是否与您的问题相符。我想知道是否由于多个模块或库或您获得的 provider_paths 资源与您预期的不同。
  • 哦,你是对的。我的 APK 中的 provider_paths 如下所示:&lt;external-path name="images" path="." /&gt; 你知道我该如何解决这个问题吗?

标签: java android kotlin androidx android-fileprovider


【解决方案1】:

您的项目有多个名为 res/xml/provider_paths.xml 的资源,而您正在结束一个与您的 FileProvider 不同的资源。

例如,您可能有一个 app/ 模块和一个 something/ 库模块 app/ 所依赖的。如果他们都有res/xml/provider_paths.xml,那么 IIRC app/“获胜”,并且它的资源被包含在内。

最简单的解决方案是将您的资源重命名为其他名称(例如,res/xml/images_provider_paths.xml)并更新您的 &lt;provider&gt; 清单条目以指向新名称。

【讨论】:

  • 我将 provider_paths.xml 重命名为 file_provider_paths.xml,但现在我收到此错误:Attribute meta-data#android.support.FILE_PROVIDER_PATHS@resource value=(@xml/provider_paths) from AndroidManifest.xml:19:17-55 is also present at AndroidManifest.xml:26:17-60 value=(@xml/file_provider_paths). Suggestion: add 'tools:replace="android:resource"' to &lt;meta-data&gt; element at AndroidManifest.xml to override. 我按照建议在我的清单文件中添加了 tools:replace="android:resource&lt;meta-data&gt;,但是错误仍然存​​在。有什么我想念的吗?
  • 我也尝试将tools:replace="android:resource 添加到&lt;application&gt;,但这导致tools:replace specified at line:10 for attribute android:resource, but no new value specified
  • @syhark:好吧,我忘了。您似乎正在使用由未阅读 this blog post of mine from 2017 的人编写的库。但是,您应该能够执行相同的技巧,只是反过来:创建一个简单的 FileProvider 子类并将您的 &lt;provider&gt; 元素切换为指向它。此外,在合并后的清单中查找 FileProvider &lt;provider&gt; 元素并确保每个元素都有不同的权限字符串。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-14
  • 1970-01-01
  • 1970-01-01
  • 2019-11-02
相关资源
最近更新 更多