协慌网

登录 贡献 社区

android.os.FileUriExposedException:通过 Intent.getData()在应用之外公开的 file:///storage/emulated/0/test.txt

当我尝试打开文件时,应用程序崩溃。它在 Android Nougat 以下运行,但是在 Android Nougat 上崩溃。仅当我尝试从 SD 卡而不是系统分区打开文件时,它才会崩溃。一些权限问题?

样例代码:

File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line

日志:

android.os.FileUriExposedException:通过 Intent.getData()在应用之外公开的 file:///storage/emulated/0/test.txt

编辑:

定位 Android 牛轧糖时,不再允许file://我们应该改用content:// URI。但是,我的应用程序需要打开根目录中的文件。有任何想法吗?

答案

如果您的targetSdkVersion >= 24 ,那么我们必须使用FileProvider类来授予对特定文件或文件夹的访问权限,以使其他应用程序可以访问它们。我们创造我们自己的类继承FileProvider ,以确保我们的 FileProvider 并不冲突与进口的依赖声明 FileProviders 描述这里

file:// URI content:// URI 的步骤:

  • <application>标记下的AndroidManifest.xml添加 FileProvider <provider> > 标记。 android:authorities属性指定唯一的权限以避免冲突,导入的依赖项可能指定${applicationId}.provider和其他常用权限。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>
    </application>
</manifest>
  • 然后在res/xml文件夹中创建一个provider_paths.xml如果尚不存在,可能需要创建一个文件夹。文件的内容如下所示。它描述了我们想共享对外部存储在根文件夹(path=".") ,其名称为external_files
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
</paths>
  • 最后一步是更改下面的代码行

    Uri photoURI = Uri.fromFile(createImageFile());

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
  • 编辑:如果您使用意图使系统打开文件,则可能需要添加以下代码行:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

请参考此处已说明的完整代码和解决方案。

FileProvider的解决方案外,还有另一种方法来解决此问题。简单的说

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

Application.onCreate() 。这样,VM 会忽略文件URI公开。

方法

builder.detectFileUriExposure()

启用文件暴露检查,如果我们未设置 VmPolicy,这也是默认行为。

我遇到一个问题,如果我使用content:// URI发送某些内容,则某些应用程序将无法理解。并且target SDK版本。在这种情况下,我的解决方案很有用。

更新:

如评论中所述,StrictMode 是诊断工具,不应用于此问题。当我一年前发布此答案时,许多应用程序只能收到 File uris。当我尝试向他们发送 FileProvider uri 时,它们只是崩溃了。现在,这在大多数应用程序中已得到修复,因此我们应该使用 FileProvider 解决方案。

如果targetSdkVersion高于24 ,则使用FileProvider授予访问权限。

创建一个 xml 文件(路径:res \ xml) provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>


AndroidManifest.xml 中添加提供程序

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

如果您使用的是androidx ,则 FileProvider 路径应为:

android:name="androidx.core.content.FileProvider"

更换

Uri uri = Uri.fromFile(fileImagePath);

Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);

编辑:当您将 URI 与Intent使用时,请确保添加以下行:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

而且你走的很好。希望能帮助到你。