我有一个例外,只有在我的应用程序中的华为设备上使用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)

这是我的清单中我的文件提供者的定义:

<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以及其他各种)将打破AndroidCompany对ContextCompat#getExternalFilesDirs(String)的调用.而不是返回Context#getExternalFilesDir(String)(即默认条目)作为数组中的第一个对象,而是返回第一个对象作为外部SD卡的路径(如果存在).

通过打破这种订购合同,这些具有外部SD卡的华为设备将在对外部文件路径根的FileProvider#getUriForFile(Context,String,File)的调用上与IllegalArgumentException异常崩溃.尽管您可以采取各种解决方案来尝试处理此问题(例如编写一个自定义FileProvider实现),但我发现最简单的方法是抓住这个问题:

> Pre-N:返回Uri#fromFile(File),由于FileUriExposedException,它不能与Android N及更高版本配合使用
> N:将文件复制到缓存路径(注意:如果在UI线程上完成,可以引入ANR),然后为复制的文件返回FileProvider#getUriForFile(Context,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,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,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,File)

有:

ContentUriProvider.getUriForFile(Context,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 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,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,file);
        }
    }
}

android – 在Huawei设备上的FileProvider错误的更多相关文章

  1. html5利用canvas实现颜色容差抠图功能

    这篇文章主要介绍了html5利用canvas实现颜色容差抠图功能,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

  2. Canvas图片分割效果的实现

    这篇文章主要介绍了Canvas图片分割效果的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5 Canvas实现放大镜效果示例

    这篇文章主要介绍了HTML5 Canvas实现放大镜效果示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. Html5 Canvas实现图片标记、缩放、移动和保存历史状态功能 (附转换公式)

    这篇文章主要介绍了Html5 Canvas实现图片标记、缩放、移动和保存历史状态功能 (附转换公式),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. html5如何在Canvas中实现自定义路径动画示例

    本篇文章主要介绍了html5如何在Canvas中实现自定义路径动画示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  6. canvas实现圆形进度条动画的示例代码

    这篇文章主要介绍了canvas实现圆形进度条动画的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. 教你使用Canvas处理图片的方法

    本篇文章主要介绍了教你使用Canvas处理图片的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  8. 手把手教你实现一个canvas智绘画板的方法

    这篇文章主要介绍了手把手教你实现一个canvas智绘画板的方法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  9. 使用canvas来完成线性渐变和径向渐变的功能的方法示例

    这篇文章主要介绍了使用canvas来完成线性渐变和径向渐变的功能的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  10. H5 canvas实现贪吃蛇小游戏

    本篇文章主要介绍了H5 canvas实现贪吃蛇小游戏,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

随机推荐

  1. bluetooth-lowenergy – Altbeacon库无法在Android 5.0上运行

    昨天我在Nexus4上获得了Android5.0的更新,并且altbeacon库停止了检测信标.似乎在监视和测距时,didEnterRegion和didRangeBeaconsInRegion都没有被调用.即使RadiusNetworks的Locate应用程序现在表现不同,一旦检测到信标的值,它们就不再得到更新,并且通常看起来好像信标超出了范围.我注意到的一点是,现在在logcat中出现以下行“B

  2. android – react-native动态更改响应者

    我正在使用react-native进行Android开发.我有一个视图,如果用户长按,我想显示一个可以拖动的动画视图.我可以使用PanResponder实现这一点,它工作正常.但我想要做的是当用户长按时,用户应该能够继续相同的触摸/按下并拖动新显示的Animated.View.如果您熟悉Google云端硬盘应用,则它具有类似的功能.当用户长按列表中的任何项目时,它会显示可拖动的项目.用户可以直接拖

  3. android – 是否有可能通过使用与最初使用的证书不同的证书对其进行签名来发布更新的应用程序

    是否可以通过使用与最初使用的证书不同的证书进行签名来发布Android应用程序的更新?我知道当我们尝试将这样的构建上传到市场时,它通常会给出错误消息.但有没有任何出路,比如将其标记为主要版本,指定市场中的某个地方?解决方法不,你不能这样做.证书是一种工具,可确保您是首次上传应用程序的人.所以总是备份密钥库!

  4. 如何检测Android中是否存在麦克风?

    ..所以我想在让用户访问语音输入功能之前检测麦克风是否存在.如何检测设备上是否有麦克风.谢谢.解决方法AndroidAPI参考:hasSystemFeature

  5. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

  6. android – 获得一首歌的流派

    我如何阅读与歌曲相关的流派?我可以读这首歌,但是如何抓住这首歌的流派,它存放在哪里?解决方法检查此代码:

  7. android – 使用textShadow折叠工具栏

    我有一个折叠工具栏的问题,在展开状态我想在文本下面有一个模糊的阴影,我使用这段代码:用:我可以更改textColor,它可以工作,但阴影不起作用.我为阴影尝试了很多不同的值.是否可以为折叠文本投射阴影?

  8. android – 重用arm共享库

    我已经建立了armarm共享库.我有兴趣重用一个函数.我想调用该函数并获得返回值.有可能做这样的事吗?我没有任何头文件.我试过这个Android.mk,我把libtest.so放在/jni和/libs/armeabi,/lib/armeabi中.此时我的cpp文件编译,但现在是什么?我从objdump知道它的名字编辑:我试图用这个android.mk从hello-jni示例中添加prebuild库:它工作,但libtest.so相同的代码显示以下错误(启动时)libtest.so存在于libhello-j

  9. android – 为NumberPicker捕获键盘’Done’

    我有一个AlertDialog只有一些文本,一个NumberPicker,一个OK和一个取消.(我知道,这个对话框还没有做它应该保留暂停和恢复状态的事情.)我想在软键盘或其他IME上执行“完成”操作来关闭对话框,就像按下了“OK”一样,因为只有一个小部件可以编辑.看起来处理IME“Done”的最佳方法通常是在TextView上使用setonEditorActionListener.但我没有任何Te

  10. android – 想要在调用WebChromeClient#onCreateWindow时知道目标URL

    当我点击一个带有target=“_blank”属性的超链接时,会调用WebChromeClient#onCreateWindow,但我找不到新的窗口将打开的新方法?主页url是我唯一能知道的东西?我想根据目标网址更改应用行为.任何帮助表示赞赏,谢谢!

返回
顶部