Android 7.0后 播放U盘中的视频文件的解决方案
背景介绍:
TV项目的系统是Android N,里面有个产品需求:是做一个视频文件管理器,检测到U盘插入之后,读取里面的视频文件,展示出来,并且支持点击播放,就是如下样式(项目实际运行展示的效果)
我们知道,如果要播放一个视频文件,必须要给播放器 设置一个路径或者Uri,一般是Uri uri=Uri.parse(…)或者是 Uri uri=Uri.fromFile(…)等,如果是这样的话,在Android N上及其之后会抛出 FileUriExposedException 异常
这是因为在Android N 之后,Android 执行的StrictMode API 政策对外部应用公开file://Uri,如果包含这个uri的intent离开当前应用会报FileUriExposedException 异常,而如果在应用间共享文件,应该用content://Uri,并授予Uri临时访问权限,这个具体可以查看 官网 点击这里
按照官网讲解步骤,我们要用到FileProvider类,它是ContentProvider的子类,要按照以下几个步骤去做:
【1】、定义一个FileProvider ,并注册。
【2】、指定要访问的文件路径位置。
【3】、生成文件的Uri。
【4】、赋予Uri临时访问权限。
【5】、向访问程序提供Uri。
具体怎么弄,我就不讲了,官网讲的已经非常详细了,这个也不是本篇博客的重点,我重点讲下如何给FileProvider配置 需要访问的文件。
在 res 目录下 新建xml目录,建一个file_paths.xml。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="files" path="" />
<cache-path name="cache" path="" />
<external-path name="external" path="" />
<external-files-path name="name" path="path" />
<external-cache-path name="name" path="path" />
</paths>
</paths>
具体的含义如下,截图来自官网。
节点 配置比如 代表文件第一级路径,里面的path代表子目录。
这几个含义对应的路径为:
getCacheDir()方法用于获取/data/data//cache目录
getFilesDir()方法用于获取/data/data//files目录
Context.getExternalFilesDir()方法可以获取到 SDCard/Android/data/你的应用的包名/files/ 目录
Context.getExternalCacheDir()方法可以获取到 SDCard/Android/data/你的应用包名/cache/目录
扫视了一下,我怎么也没有找到UsbDisk的配置访问路径啊,官网介绍读了两遍还是没有看到啊,这个怎么办?无奈的我还是一如既往的去 源码中寻找答案,开启源码解读模式 (FileProvider.java)
/**
* Parse and return {@link PathStrategy} for given authority as defined in
* {@link #META_DATA_FILE_PROVIDER_PATHS} {@code <meta-data>}.
*
* @see #getPathStrategy(Context, String)
*/
private static PathStrategy parsePathStrategy(Context context, String authority)
throws IOException, XmlPullParserException {
// 省略部分代码
int type;
// 解析我们在res下面配置的 file_paths.xml 文件
while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) {
final String tag = in.getName();
final String name = in.getAttributeValue(null, ATTR_NAME);
String path = in.getAttributeValue(null, ATTR_PATH);
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));
}
}
}
return strat;
}
根据我们配置的不同xml节点,去获得不同的访问路径,在这里我终于发现了一个强大的节点,就是TAG_ROOT_PATH
“root-path” 经过分析,代表的设备文件系统的根节点,就是我们adb shell 进去之后的根节点,知道这个之后,访问U盘就好配置了,如下:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="externalfiles" path="/storage/"/>
</paths>
path 配置成 /storage/,这样就可以访问到U盘文件了,最终成功解决了问题,有点小激动~~!