FileProvider - java.lang.IllegalArgumentException: 找不到配置的根包含
FileProvider - java.lang.IllegalArgumentException: Failed to find configured root that contains
我正在尝试使用 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)
}
我做了一些调试,似乎 FileProvider
getUriForFile(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(文件文件):
@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();
}
我已经尝试过的东西:
- 将
<root-path name="root" path="." />
添加到 <paths>
- 尝试
filesDir
而不是 cacheDir
- 运行 phone 而不是模拟器
上的应用程序
您的项目有多个名为 res/xml/provider_paths.xml
的资源,而您最终得到的资源是供其他 FileProvider
使用的资源。
例如,您可能有 app/
模块和 app/
依赖的 something/
库模块。如果它们都具有 res/xml/provider_paths.xml
,则 IIRC app/
“获胜”,并且其资源被包含在内。
最简单的解决方案是将您的资源重命名为其他名称(例如,res/xml/images_provider_paths.xml
)并更新您的 <provider>
清单条目以指向新名称。
我正在尝试使用 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)
}
我做了一些调试,似乎 FileProvider
getUriForFile(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(文件文件):
@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();
}
我已经尝试过的东西:
- 将
<root-path name="root" path="." />
添加到<paths>
- 尝试
filesDir
而不是cacheDir
- 运行 phone 而不是模拟器 上的应用程序
您的项目有多个名为 res/xml/provider_paths.xml
的资源,而您最终得到的资源是供其他 FileProvider
使用的资源。
例如,您可能有 app/
模块和 app/
依赖的 something/
库模块。如果它们都具有 res/xml/provider_paths.xml
,则 IIRC app/
“获胜”,并且其资源被包含在内。
最简单的解决方案是将您的资源重命名为其他名称(例如,res/xml/images_provider_paths.xml
)并更新您的 <provider>
清单条目以指向新名称。