Android - 将原始资产中的文件保存到外部存储以供其他应用访问,导致找不到内容根异常

Posted

技术标签:

【中文标题】Android - 将原始资产中的文件保存到外部存储以供其他应用访问,导致找不到内容根异常【英文标题】:Android - Save a file from raw assets to external storage for others apps to access gives failed to find content root exception 【发布时间】:2021-03-20 08:08:32 【问题描述】:

我在我的原始资产目​​录中有一个文件,我正在尝试将其保存到共享手机存储中,该文件将允许其他应用打开和查看此视频。。 p>

我可以使用以下代码将文件保存到本地应用存储或外部私有应用存储。

    Intent in = new Intent(Intent.ACTION_VIEW);

    // using getFilesDir() below causes this error: 
    File videoFile = new File(getExternalFilesDirs(null)[1], "talking.mp4");

    // check if we have already copied file over
    if (!videoFile.exists()) 

        try 
            InputStream is = getResources().openRawResource(R.raw.talking);
            boolean newFile = videoFile.createNewFile();
            if (!newFile) 
                throw new Exception("Failed to create file");
            
            OutputStream os = new FileOutputStream(videoFile);
            byte[] data = new byte[is.available()];
            is.read(data);
            os.write(data);
            is.close();
            os.close();
         catch (IOException e) 
            Log.w("ExternalStorage", "Error writing " + videoFile, e);
            Toast.makeText(this, "Failed to copy talking.mp4", Toast.LENGTH_LONG).show();
            return;
         catch (Exception e) 
            e.printStackTrace();
        
    

    // Line below 135  
    Uri uri =  FileProvider.getUriForFile(getApplicationContext(), getPackageName(), videoFile);
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Problematic Line

    // type of file to view
    in.setDataAndType(uri, "video/*");

    // ask some other app to deal with it
    startActivity(in);

问题是我不断收到这个错误:

Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains 
    /storage/1B12-3A08/android/data/$getPackageName()/files/talking.mp4

        at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:744)
        at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
        at $getPackageName().MainActivity.btnPlayIntentClicked(MainActivity.java:135)

在 link 和 FileProvider 的帮助下进行了一些快速研究,我将其添加到我的清单中:

<application ... >
    //...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="$getPackageName()"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths" />
    </provider> 
</application>

并在res/xml/paths.xml拥有以下资源:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="my_files" path="." />
    <external-files-path name="my_files" path="." />
    <external-path name="my_files" path="." />
</paths>

问题:

配置了可用的内容根之后,我应该能够将文件从原始资源复制到外部私有存储并请求 Android(通过隐式意图)使用例如适用于 Android 的 MX 播放器或 VLC。

内容根配置缺少什么? (我添加了 external-files-pathexternal-path 以示好衡量)


@Commonsware 回复后更新

我变了

File videoFile = new File(getExternalFilesDirs(null)[1], "talking.mp4");

File videoFile = new File(getExternalFilesDir(null), "talking.mp4");

这也只是

File videoFile = new File(getExternalFilesDirs(null)[0], "talking.mp4");

但这给出了同样的错误,只是文件的另一个路径前缀:

Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/$getPackageName()/files/talking.mp4

我能够让文件内容根目录工作并使用 Intent 传递文件 Uri,它提供了使用 VLC(适用于 Android)和其他一些打开的选项,问题不在于应用程序可以播放文件。我不记得我是怎么做到的


更新 2

@BlackApps 建议更改res/xml/paths.xml 文件中的路径名解决了这个问题

最初我有不同的名字,但没有奏效,也没有考虑在一些实验后将它们改回来。

导致问题的原因:

内容根路径在应用启动时通过this 函数加载:

FileProvider.parsePathStrategy(Context, String)

在内部,它通过使用它们相应的文件提供程序路径来创建所有路径的缓存(映射)以提供额外的上下文(将路径附加到&lt;external-file/&gt;&lt;files-path/&gt; 中的指定路径。给定输入:

<external-file name="some_name" path="MyAwesomeAssets"/>

将导致 (Environment.getExternalStorageDirectory()) 目标路径 (Environment.getExternalStorageDirectory()) 附加到路径“MyAweomseAssets”,从而导致输出路径为 /storage/emulated/0/MyAwesomeAssets

然后将其添加到缓存中,但在我的情况下,由于我对每个名称都有相同的名称,因此它会简单地覆盖最后一个路径,即 FIFO 方法。

因此,当我尝试在我指定的内容根目录中获取文件的 Uri 时,只会找到一个,这将是 res/xml/paths.xml 中定义的最后一个。

【问题讨论】:

name="my_files" 你的名字是同名的三倍。让它们与众不同。 @blackapps 是的 - 解决了这个问题。之前给它起了不同的名字,但这也无济于事。无论如何,您的回答解决了问题。 【参考方案1】:

FileProvider 不支持可移动存储。将getExternalFilesDirs(null)[1] 更改为getExternalFilesDir(null),或者您需要编写自己的ContentProvider 以从所需位置(或直接从资产,如果您愿意)提供内容。

【讨论】:

@CybeX: &lt;external-files-path&gt; 映射到 getExternalFilesDir(null),因此不清楚为什么您的设置不起作用。您可能需要检查您的合并清单并确认您的 &lt;provider&gt; 元素存在并且未更改 - 如果某个库恰好也使用您的相同权限字符串,那么它的元数据可能由于某种原因被拾取。 记录一下,这是一个新的VM - 检查清单和提供程序如上所示 - 我很困惑。

以上是关于Android - 将原始资产中的文件保存到外部存储以供其他应用访问,导致找不到内容根异常的主要内容,如果未能解决你的问题,请参考以下文章

将 Android 11 中的文件保存到外部存储(SDK 30)

Android 文件保存

Android将文件保存到外部存储

asserts文件存到外部SD卡里

将Android中的位图保存为外部存储中的JPEG文件夹

将 SQLite 数据库从 sdcard 导入到 android - 没有资产文件夹