Android 7.0后 播放U盘中的视频文件的解决方案

背景介绍:
TV项目的系统是Android N,里面有个产品需求:是做一个视频文件管理器,检测到U盘插入之后,读取里面的视频文件,展示出来,并且支持点击播放,就是如下样式(项目实际运行展示的效果)
Android 7.0后 播放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代表子目录。
Android 7.0后 播放U盘中的视频文件的解决方案
这几个含义对应的路径为:
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盘文件了,最终成功解决了问题,有点小激动~~!