- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
在我的应用中使用 FileProvider.getUriForFile
时出现仅在华为设备上发生的异常:
Exception: java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/<card name>/Android/data/<app package>/files/.export/2016-10-06 13-22-33.pdf
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(SourceFile:711)
at android.support.v4.content.FileProvider.getUriForFile(SourceFile:400)
这是我的 list 中文件提供程序的定义:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
配置路径的资源文件:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="external_files" path="" />
</paths>
您知道这个问题的原因以及为什么它只发生在华为设备上吗?鉴于我没有华为设备,我该如何调试?
更新:
我在我的应用程序中添加了更多日志,但在这些设备上同时打印 ContextCompat.getExternalFilesDirs
和 context.getExternalFilesDir
时得到了一些不一致的结果:
ContextCompat.getExternalFilesDirs:
/storage/emulated/0/Android/data/<package>/files
/storage/sdcard1/Android/data/<package>/files
context.getExternalFilesDir:
/storage/sdcard1/Android/data/<package>/files
这与 ContextCompat.getExternalFilesDirs
的文档不一致,该文档指出 返回的第一个路径与 getExternalFilesDir(String) 相同
这解释了问题,因为我在代码中使用 context.getExternalFilesDir
而 FileProvider
使用 ContextCompat.getExternalFilesDirs
。
最佳答案
Android N 的更新(保留下面的原始答案,并确认这种新方法在生产中有效):
正如您在更新中提到的,许多华为设备型号(例如 KIW-L24、ALE-L21、ALE-L02、PLK-L01 和其他各种型号)违反了 Android 对 ContextCompat#getExternalFilesDirs(String)
的调用契约(Contract)。 .而不是返回 Context#getExternalFilesDir(String)
(即默认条目)作为数组中的第一个对象,而是返回第一个对象作为外部 SD 卡的路径(如果存在)。
如果违反此订购契约(Contract),这些带有外部 SD 卡的华为设备将崩溃并显示 IllegalArgumentException
调用FileProvider#getUriForFile(Context, String, File)
对于 external-files-path
根。虽然您可以寻求多种解决方案来尝试处理此问题(例如编写自定义 FileProvider
实现),但我发现最简单的方法是发现此问题并且:
Uri#fromFile(File)
,由于 FileUriExposedException
,它不适用于 Android N 及更高版本cache-path
(注意:如果在 UI 线程上完成,这可能会引入 ANR)然后返回 FileProvider#getUriForFile(Context, String, File)
对于复制的文件(即完全避免错误)可以在下面找到完成此操作的代码:
public class ContentUriProvider {
private static final String HUAWEI_MANUFACTURER = "Huawei";
public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file) {
if (HUAWEI_MANUFACTURER.equalsIgnoreCase(Build.MANUFACTURER)) {
Log.w(ContentUriProvider.class.getSimpleName(), "Using a Huawei device Increased likelihood of failure...");
try {
return FileProvider.getUriForFile(context, authority, file);
} catch (IllegalArgumentException e) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Log.w(ContentUriProvider.class.getSimpleName(), "Returning Uri.fromFile to avoid Huawei 'external-files-path' bug for pre-N devices", e);
return Uri.fromFile(file);
} else {
Log.w(ContentUriProvider.class.getSimpleName(), "ANR Risk -- Copying the file the location cache to avoid Huawei 'external-files-path' bug for N+ devices", e);
// Note: Periodically clear this cache
final File cacheFolder = new File(context.getCacheDir(), HUAWEI_MANUFACTURER);
final File cacheLocation = new File(cacheFolder, file.getName());
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(file);
out = new FileOutputStream(cacheLocation); // appending output stream
IOUtils.copy(in, out);
Log.i(ContentUriProvider.class.getSimpleName(), "Completed Android N+ Huawei file copy. Attempting to return the cached file");
return FileProvider.getUriForFile(context, authority, cacheLocation);
} catch (IOException e1) {
Log.e(ContentUriProvider.class.getSimpleName(), "Failed to copy the Huawei file. Re-throwing exception", e1);
throw new IllegalArgumentException("Huawei devices are unsupported for Android N", e1);
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
}
}
}
} else {
return FileProvider.getUriForFile(context, authority, file);
}
}
}
连同 file_provider_paths.xml
:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="public-files-path" path="." />
<cache-path name="private-cache-path" path="." />
</paths>
一旦您创建了这样的类,请将您的调用替换为:
FileProvider.getUriForFile(Context, String, File)
与:
ContentUriProvider.getUriForFile(Context, String, File)
坦率地说,我不认为这是一个特别优雅的解决方案,但它确实允许我们使用正式记录的 Android 行为而无需做任何过于激烈的事情(例如编写自定义 FileProvider
实现)。我已经在生产中对此进行了测试,因此我可以确认它可以解决这些华为崩溃问题。对我来说,这是最好的方法,因为我不想花太多时间来解决明显是制造商的缺陷。
从之前的华为设备更新到 Android N:
由于 FileUriExposedException
,这不适用于 Android N 及更高版本,但我还没有遇到在 Android N 上出现这种错误配置的华为设备。
public class ContentUriProvider {
private static final String HUAWEI_MANUFACTURER = "Huawei";
public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file) {
if (HUAWEI_MANUFACTURER.equalsIgnoreCase(Build.MANUFACTURER) && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Log.w(ContentUriProvider.class.getSimpleName(), "Using a Huawei device on pre-N. Increased likelihood of failure...");
try {
return FileProvider.getUriForFile(context, authority, file);
} catch (IllegalArgumentException e) {
Log.w(ContentUriProvider.class.getSimpleName(), "Returning Uri.fromFile to avoid Huawei 'external-files-path' bug", e);
return Uri.fromFile(file);
}
} else {
return FileProvider.getUriForFile(context, authority, file);
}
}
}
关于android - 华为设备上的 FileProvider getUriForFile() 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39895579/
我使用 Intent 发送文件的代码不适用于所有文件源,我还找不到解决方案: 我的应用程序已注册用于打开文件,因此当我在 ES 文件资源管理器中选择一个文件时, Intent 如下所示: intent
我正在制作一个应用程序,我需要拍照,然后将一些数据保存到数据库中,但是当我尝试从照片中获取 URI 时出现此错误: java.lang.IllegalArgumentException: Failed
FileProvider.getUriForFile 返回一个空对象引用,我不知道为什么。这以前有效。以下错误是我得到的。我还在错误下方添加了我的代码。 Process: com.example.jo
所以我正在开发一个应用程序,我会在其中拍照并尝试将其保存到应用程序的内部存储空间中。我在使用文件提供程序时遇到问题。我已经看过许多关于堆栈溢出的问题,但如果可能的话,我想得到更详细的解释。 我也按照谷
首先要提的是,问题的答案here不帮忙。 源代码如下所示: Intent intent = new Intent("com.mycompany.action.PICK_FILE"); Strin
在我的应用中,尝试使用相机应用捕捉图像。我正在更新我的应用程序以在指定用于保存图像的文件路径时使用 FileProvider.getUriForFile,因为 Uri.fromFile 已被弃用。 使
你能帮我调试我的应用程序吗,它构建时没有错误,但是一旦我单击“更改个人资料图片”加载相机(就像 Instagram 上用于更改个人资料图片的按钮),我的应用程序就会停止。 我将不胜感激任何帮助,谢谢
我已经更新了我的项目以实现Providers。整个实现似乎是正确的,但我在 FileProvider 类中收到了一个 StringIndexOutOfBoundsException 异常。 在我的例子
我正在尝试编写我的第一个 Android 应用程序,它将涉及拍照和处理照片。 我在网上看了几个教程后整理了一些代码,但是每当单击按钮时我都会收到以下 NullPointerException: 10-
我按照 android developer reference on FileProviders 上的示例代码进行操作但它不会工作。 我在 Manifest 的文件提供程序定义中设置了路径 files
在我的应用中使用 FileProvider.getUriForFile 时出现仅在华为设备上发生的异常: Exception: java.lang.IllegalArgumentException:
长话短说 我想通过 FileProvider 获取(读取/生成)文件 Uri - 路径如下 - 但不知道如何: val file = File("/external/file/9359") // ho
我正在尝试实现一个 FileProvider 以允许我与电子邮件和其他应用程序共享本地私有(private)图像。我一直按照 Android 开发人员指南的说明进行操作,并深入了解必须使用“getUr
目前,当文件位于外部 SD 上时,FileProvider getUriForFile 方法会生成 IllegalArgumentException 当文件在设备内存中(在/storage/emula
我在 Kotlin 中遇到了奇怪的行为 创建像这样的自定义文件提供程序时 import android.support.v4.content.FileProvider class MyFileProv
在我的 Andoroid 应用程序中,我想让用户使用用户喜欢的任何发送方法(例如电子邮件、Skype、Viber、蓝牙等)发送我的应用程序生成的文件我正在使用 Intent.ACTION_SEND 如
我正在尝试实现一个相机 Activity ,用户可以在其中拍照并将其保存到手机上。 错误: java.lang.IllegalArgumentException: Failed to find con
下面的代码用于尝试文件提供程序,为 Android Nougat 做准备: list 文件:
我一直在尝试关注 Android tutorial关于共享文件。我这样设置 FileProvider: 在主 list xml 上: res/xml/fi
我有来自 this 的代码统一回答代码以安装 apk,但它在 Android 7.0 中不起作用,因为不再支持 Uri.fromfile 并且现在应该使用 FileProvider.getUriFor
我是一名优秀的程序员,十分优秀!