本文只针对特定机型,如果你发现出现问题的全是那一种机型,那也许可以参考这里,具体的实际上看链接就可以了,我写这些主要是为了给自己记下来
参考链接:StackOverFlow
里面给了两种方法吧,一种是把文件地址全部拷贝到缓存地址里面去,一种是全部用ContextCompat这个兼容类的方法
原因:部分手机ContextCompat.getExternalFilesDirs(Context, String)
方法获取到的存储地址和Context.getExternalFilesDir
方法获取到的不同
(Context那个并不是静态方法,我这么写而已)
ContextCompat.getExternalFilesDirs(Context, String)
在FileProvider
的 FileProvider.parsPathStrategy
中也有调用,具体代码:
// 将 name 和 root 赋值给mRoot ,而 mRoot 会在 getUriForFile() 中使用,后者是发生getUriForFile即发生crash的地点。
private static PathStrategy parsePathStrategy(Context context, String authority)
throws IOException, XmlPullParserException {
...
if (TAG_EXTERNAL_FILES.equals(tag)) {
File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
if (externalFilesDirs.length > 0) {
target = externalFilesDirs[0];
}
...
if (target != null) {
strat.addRoot(name, buildPath(target, path));
}
return strat;
}
省略部分的具体代码:
File target = null;
if (TAG_ROOT_PATH.equals(tag)) {
target = DEVICE_ROOT;
} else if (TAG_FILES_PATH.equals(tag)) {
target = context.getFilesDir();
} else if (TAG_CACHE_PATH.equals(tag)) {
target = context.getCacheDir();
} else if (TAG_EXTERNAL.equals(tag)) {
target = Environment.getExternalStorageDirectory();
} else if (TAG_EXTERNAL_FILES.equals(tag)) {
File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
if (externalFilesDirs.length > 0) {
target = externalFilesDirs[0];
}
} else if (TAG_EXTERNAL_CACHE.equals(tag)) {
File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(context);
if (externalCacheDirs.length > 0) {
target = externalCacheDirs[0];
}
}
if (target != null) {
strat.addRoot(name, buildPath(target, path));
}
可见只有externalfiles搞特殊,别的用的都是context中的方法,它用ContextCompat,如果二者具体得到的地址不同,那么自然会crash
具体地点
public Uri getUriForFile(File file) {
...
Map.Entry<String, File> mostSpecific = null;
for (Map.Entry<String, File> root : mRoots.entrySet()) {
final String rootPath = root.getValue().getPath();
if (path.startsWith(rootPath)
// 问题出现的地方,这里path来自文件,root来自mRoot,如果起始不同就会为空
&& (mostSpecific == null
|| rootPath.length() > mostSpecific.getValue().getPath().length())) {
mostSpecific = root;
}
}
if (mostSpecific == null) {
throw new IllegalArgumentException(
"Failed to find configured root that contains " + path);
}
...
}
总结
问题发生的原因是getUriForFile
方法的结果来自
ContextCompat.getExternalFilesDirs(context, null)[0]
这个方法的位置0默认和Context.getExternalFilesDir
相同,但是有的手机上这里会有差异,结果导致了问题出现。
所以实际上选取一个简单的办法,使用ContextCompat.getExternalFilesDirs(context, null)
当作File来获取Uri就可以解决问题。
测试
-
File file = mApplication.getContext().getExternalFilesDir(null);
/storage/emulated/0/Android/data/(app)/files
-
File file2 = ContextCompat.getExternalFilesDirs(IMApplication.getContext(), null)[0];
/storage/emulated/0/Android/data/(app)/files
可见大部分情况下这两个方法获得的结果都是一样的,可惜没能复现,所以也无从得知出现问题的手机这两个路径会差到哪里去。